mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-25 23:53:09 +02:00
Compare commits
24 commits
990e53fee4
...
f93a8e2d38
Author | SHA1 | Date | |
---|---|---|---|
|
f93a8e2d38 | ||
|
d3d0e510d8 | ||
|
65a1bc2199 | ||
|
469c3f3f1a | ||
|
75bce2228f | ||
|
65a7623944 | ||
|
b2d520369f | ||
|
441523b6d4 | ||
|
fc06494908 | ||
|
f091b84c6e | ||
|
4bc517ca89 | ||
|
bb3694bfed | ||
|
1bb5021c54 | ||
|
a981bca7a3 | ||
|
3a3a051bb5 | ||
|
7684f45a5e | ||
|
25948c9232 | ||
|
83f1c596b0 | ||
|
93bd4dc8fe | ||
|
074c47fdf8 | ||
|
c6e3ec07d1 | ||
|
e93e73673a | ||
|
b0fcc11d9e | ||
|
83e9057b27 |
49 changed files with 655 additions and 298 deletions
2
.npmrc
2
.npmrc
|
@ -1,2 +1,2 @@
|
|||
@sharkey:registry=https://git.joinsharkey.org/api/packages/Sharkey/npm/
|
||||
@transfem-org:registry=https://activitypub.software/api/v4/packages/npm/
|
||||
engine-strict = true
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
---
|
||||
|
||||
<a href="https://joinsharkey.org">
|
||||
<a href="https://fedidb.org/software/sharkey">
|
||||
<img src="https://custom-icon-badges.herokuapp.com/badge/find_an-instance-acea31?logoColor=acea31&style=for-the-badge&logo=sharkey&labelColor=363B40" alt="find an instance"/></a>
|
||||
|
||||
<a href="https://docs.joinsharkey.org/docs/install/fresh/">
|
||||
|
|
|
@ -553,6 +553,8 @@ objectStorageUseProxy: "Connect over Proxy"
|
|||
objectStorageUseProxyDesc: "Turn this off if you are not going to use a Proxy for API connections"
|
||||
objectStorageSetPublicRead: "Set \"public-read\" on upload"
|
||||
s3ForcePathStyleDesc: "If s3ForcePathStyle is enabled, the bucket name has to included in the path of the URL as opposed to the hostname of the URL. You may need to enable this setting when using services such as a self-hosted Minio instance."
|
||||
deeplFreeMode: "Use DeepLX-JS (No Auth Key)"
|
||||
deeplFreeModeDescription: "Need Help? Check our documentation to know how to setup DeepLX-JS."
|
||||
serverLogs: "Server logs"
|
||||
deleteAll: "Delete all"
|
||||
showFixedPostForm: "Display the posting form at the top of the timeline"
|
||||
|
|
8
locales/index.d.ts
vendored
8
locales/index.d.ts
vendored
|
@ -2240,6 +2240,14 @@ export interface Locale extends ILocale {
|
|||
* s3ForcePathStyleを有効にすると、バケット名をURLのホスト名ではなくパスの一部として指定することを強制します。セルフホストされたMinioなどの使用時に有効にする必要がある場合があります。
|
||||
*/
|
||||
"s3ForcePathStyleDesc": string;
|
||||
/**
|
||||
* DeepLX-JS を使用する (認証キーなし)
|
||||
*/
|
||||
"deeplFreeMode": string;
|
||||
/**
|
||||
* ヘルプが必要ですか? DeepLX-JSのセットアップ方法については、ドキュメントを参照してください。
|
||||
*/
|
||||
"deeplFreeModeDescription": string;
|
||||
/**
|
||||
* サーバーログ
|
||||
*/
|
||||
|
|
|
@ -556,6 +556,8 @@ objectStorageUseProxy: "Proxyを利用する"
|
|||
objectStorageUseProxyDesc: "API接続にproxyを利用しない場合はオフにしてください"
|
||||
objectStorageSetPublicRead: "アップロード時に'public-read'を設定する"
|
||||
s3ForcePathStyleDesc: "s3ForcePathStyleを有効にすると、バケット名をURLのホスト名ではなくパスの一部として指定することを強制します。セルフホストされたMinioなどの使用時に有効にする必要がある場合があります。"
|
||||
deeplFreeMode: "DeepLX-JS を使用する (認証キーなし)"
|
||||
deeplFreeModeDescription: "ヘルプが必要ですか? DeepLX-JSのセットアップ方法については、ドキュメントを参照してください。"
|
||||
serverLogs: "サーバーログ"
|
||||
deleteAll: "全て削除"
|
||||
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
|
||||
|
|
18
packages/backend/migration/1706232992000-deeplx.js
Normal file
18
packages/backend/migration/1706232992000-deeplx.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Deeplx1706232992000 {
|
||||
name = 'Deeplx1706232992000';
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "deeplFreeMode" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "deeplFreeInstance" character varying(1024)`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplFreeMode"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplFreeInstance"`);
|
||||
}
|
||||
}
|
|
@ -83,7 +83,7 @@
|
|||
"@nestjs/core": "10.2.10",
|
||||
"@nestjs/testing": "10.2.10",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sharkey/sfm-js": "0.24.4",
|
||||
"@transfem-org/sfm-js": "0.24.4",
|
||||
"@simplewebauthn/server": "9.0.0",
|
||||
"@sinonjs/fake-timers": "11.2.2",
|
||||
"@smithy/node-http-handler": "2.1.10",
|
||||
|
|
|
@ -13,7 +13,7 @@ import { intersperse } from '@/misc/prelude/array.js';
|
|||
import type { IMentionedRemoteUsers } from '@/models/Note.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js';
|
||||
import type * as mfm from '@sharkey/sfm-js';
|
||||
import type * as mfm from '@transfem-org/sfm-js';
|
||||
|
||||
const treeAdapter = TreeAdapter.defaultTreeAdapter;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { setImmediate } from 'node:timers/promises';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { In, DataSource, IsNull, LessThan } from 'typeorm';
|
||||
import * as Redis from 'ioredis';
|
||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
|
@ -328,6 +328,9 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH);
|
||||
}
|
||||
data.text = data.text.trim();
|
||||
if (data.text === '') {
|
||||
data.text = null;
|
||||
}
|
||||
} else {
|
||||
data.text = null;
|
||||
}
|
||||
|
@ -807,7 +810,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
|
||||
const muted = isUserRelated(note, userIdsWhoMeMuting);
|
||||
|
||||
if (!isThreadMuted || !muted) {
|
||||
if (!isThreadMuted && !muted) {
|
||||
nm.push(data.reply.userId, 'reply');
|
||||
this.globalEventService.publishMainStream(data.reply.userId, 'reply', noteObj);
|
||||
|
||||
|
@ -842,7 +845,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
|
||||
const muted = isUserRelated(note, userIdsWhoMeMuting);
|
||||
|
||||
if (!isThreadMuted || !muted) {
|
||||
if (!isThreadMuted && !muted) {
|
||||
nm.push(data.renote.userId, type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { setImmediate } from 'node:timers/promises';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { DataSource, In, IsNull, LessThan } from 'typeorm';
|
||||
import * as Redis from 'ioredis';
|
||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
|
@ -46,6 +46,11 @@ import { MetaService } from '@/core/MetaService.js';
|
|||
import { SearchService } from '@/core/SearchService.js';
|
||||
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { isReply } from '@/misc/is-reply.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
|
||||
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
|
||||
|
||||
|
@ -206,6 +211,8 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
private activeUsersChart: ActiveUsersChart,
|
||||
private instanceChart: InstanceChart,
|
||||
private utilityService: UtilityService,
|
||||
private userBlockingService: UserBlockingService,
|
||||
private cacheService: CacheService,
|
||||
) { }
|
||||
|
||||
@bindThis
|
||||
|
@ -255,16 +262,18 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
if (data.channel != null) data.localOnly = true;
|
||||
if (data.updatedAt == null) data.updatedAt = new Date();
|
||||
|
||||
const meta = await this.metaService.fetch();
|
||||
|
||||
if (data.visibility === 'public' && data.channel == null) {
|
||||
const sensitiveWords = (await this.metaService.fetch()).sensitiveWords;
|
||||
if (this.isSensitive(data, sensitiveWords)) {
|
||||
const sensitiveWords = meta.sensitiveWords;
|
||||
if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
|
||||
data.visibility = 'home';
|
||||
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
|
||||
data.visibility = 'home';
|
||||
}
|
||||
}
|
||||
|
||||
const inSilencedInstance = this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, user.host);
|
||||
const inSilencedInstance = this.utilityService.isSilencedHost((meta).silencedHosts, user.host);
|
||||
|
||||
if (data.visibility === 'public' && inSilencedInstance && user.host !== null) {
|
||||
data.visibility = 'home';
|
||||
|
@ -296,6 +305,18 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
}
|
||||
}
|
||||
|
||||
// Check blocking
|
||||
if (data.renote && !this.isQuote(data)) {
|
||||
if (data.renote.userHost === null) {
|
||||
if (data.renote.userId !== user.id) {
|
||||
const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);
|
||||
if (blocked) {
|
||||
throw new Error('blocked');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返信対象がpublicではないならhomeにする
|
||||
if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') {
|
||||
data.visibility = 'home';
|
||||
|
@ -316,6 +337,9 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH);
|
||||
}
|
||||
data.text = data.text.trim();
|
||||
if (data.text === '') {
|
||||
data.text = null;
|
||||
}
|
||||
} else {
|
||||
data.text = null;
|
||||
}
|
||||
|
@ -361,6 +385,14 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
}
|
||||
}
|
||||
|
||||
if (user.host && !data.cw) {
|
||||
await this.federatedInstanceService.fetch(user.host).then(async i => {
|
||||
if (i.isNSFW) {
|
||||
data.cw = 'Instance is marked as NSFW';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const update: Partial<MiNote> = {};
|
||||
if (data.text !== oldnote.text) {
|
||||
update.text = data.text;
|
||||
|
@ -557,7 +589,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
} else {
|
||||
this.globalEventService.publishNoteStream(note.id, 'updated', {
|
||||
cw: note.cw,
|
||||
text: note.text!
|
||||
text: note.text!,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -587,7 +619,15 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
},
|
||||
});
|
||||
|
||||
if (!isThreadMuted) {
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
] = data.reply.userId ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(data.reply.userId),
|
||||
]) : [new Set<string>()];
|
||||
|
||||
const muted = isUserRelated(note, userIdsWhoMeMuting);
|
||||
|
||||
if (!isThreadMuted && !muted) {
|
||||
nm.push(data.reply.userId, 'reply');
|
||||
this.globalEventService.publishMainStream(data.reply.userId, 'reply', noteObj);
|
||||
|
||||
|
@ -603,12 +643,29 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
|
||||
// If it is renote
|
||||
if (data.renote) {
|
||||
const type = data.text ? 'quote' : 'renote';
|
||||
const type = this.isQuote(data) ? 'quote' : 'renote';
|
||||
|
||||
// Notify
|
||||
if (data.renote.userHost === null) {
|
||||
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
||||
where: {
|
||||
userId: data.renote.userId,
|
||||
threadId: data.renote.threadId ?? data.renote.id,
|
||||
},
|
||||
});
|
||||
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
] = data.renote.userId ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(data.renote.userId),
|
||||
]) : [new Set<string>()];
|
||||
|
||||
const muted = isUserRelated(note, userIdsWhoMeMuting);
|
||||
|
||||
if (!isThreadMuted && !muted) {
|
||||
nm.push(data.renote.userId, type);
|
||||
}
|
||||
}
|
||||
|
||||
// Publish event
|
||||
if ((user.id !== data.renote.userId) && data.renote.userHost === null) {
|
||||
|
@ -657,7 +714,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
this.relayService.deliverToRelays(user, noteActivity);
|
||||
}
|
||||
|
||||
dm.execute();
|
||||
trackPromise(dm.execute());
|
||||
})();
|
||||
}
|
||||
//#endregion
|
||||
|
@ -686,28 +743,9 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
private isSensitive(note: Option, sensitiveWord: string[]): boolean {
|
||||
if (sensitiveWord.length > 0) {
|
||||
const text = note.cw ?? note.text ?? '';
|
||||
if (text === '') return false;
|
||||
const matched = sensitiveWord.some(filter => {
|
||||
// represents RegExp
|
||||
const regexp = filter.match(/^\/(.+)\/(.*)$/);
|
||||
// This should never happen due to input sanitisation.
|
||||
if (!regexp) {
|
||||
const words = filter.split(' ');
|
||||
return words.every(keyword => text.includes(keyword));
|
||||
}
|
||||
try {
|
||||
return new RE2(regexp[1], regexp[2]).test(text);
|
||||
} catch (err) {
|
||||
// This should never happen due to input sanitisation.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (matched) return true;
|
||||
}
|
||||
return false;
|
||||
private isQuote(note: Option): note is Option & { renote: MiNote } {
|
||||
// sync with misc/is-quote.ts
|
||||
return !!note.renote && (!!note.text || !!note.cw || (!!note.files && !!note.files.length) || !!note.poll);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
|
@ -720,7 +758,15 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
},
|
||||
});
|
||||
|
||||
if (isThreadMuted) {
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
] = u.id ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(u.id),
|
||||
]) : [new Set<string>()];
|
||||
|
||||
const muted = isUserRelated(note, userIdsWhoMeMuting);
|
||||
|
||||
if (isThreadMuted || muted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -748,7 +794,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
const user = await this.usersRepository.findOneBy({ id: note.userId });
|
||||
if (user == null) throw new Error('user not found');
|
||||
|
||||
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
|
||||
const content = data.renote && !this.isQuote(data)
|
||||
? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note)
|
||||
: this.apRendererService.renderUpdate(await this.apRendererService.renderUpNote(note, false), user);
|
||||
|
||||
|
@ -782,6 +828,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
@bindThis
|
||||
private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) {
|
||||
const meta = await this.metaService.fetch();
|
||||
if (!meta.enableFanoutTimeline) return;
|
||||
|
||||
const r = this.redisForTimelines.pipeline();
|
||||
|
||||
|
@ -825,7 +872,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
|
||||
if (note.visibility === 'followers') {
|
||||
// TODO: 重そうだから何とかしたい Set 使う?
|
||||
userListMemberships = userListMemberships.filter(x => followings.some(f => f.followerId === x.userListUserId));
|
||||
userListMemberships = userListMemberships.filter(x => x.userListUserId === user.id || followings.some(f => f.followerId === x.userListUserId));
|
||||
}
|
||||
|
||||
// TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする
|
||||
|
@ -834,7 +881,7 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
|
||||
|
||||
// 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
|
||||
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
|
||||
if (isReply(note, following.followerId)) {
|
||||
if (!following.withReplies) continue;
|
||||
}
|
||||
|
||||
|
@ -848,11 +895,12 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
// ダイレクトのとき、そのリストが対象外のユーザーの場合
|
||||
if (
|
||||
note.visibility === 'specified' &&
|
||||
note.userId !== userListMembership.userListUserId &&
|
||||
!note.visibleUserIds.some(v => v === userListMembership.userListUserId)
|
||||
) continue;
|
||||
|
||||
// 「自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合
|
||||
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) {
|
||||
if (isReply(note, userListMembership.userListUserId)) {
|
||||
if (!userListMembership.withReplies) continue;
|
||||
}
|
||||
|
||||
|
@ -870,11 +918,14 @@ export class NoteEditService implements OnApplicationShutdown {
|
|||
}
|
||||
|
||||
// 自分自身以外への返信
|
||||
if (note.replyId && note.replyUserId !== note.userId) {
|
||||
if (isReply(note)) {
|
||||
this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
|
||||
|
||||
if (note.visibility === 'public' && note.userHost == null) {
|
||||
this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r);
|
||||
if (note.replyUserHost == null) {
|
||||
this.fanoutTimelineService.push(`localTimelineWithReplyTo:${note.replyUserId}`, note.id, 300 / 10, r);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.fanoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { MfmService } from '@/core/MfmService.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { createPublicKey, randomUUID } from 'node:crypto';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { In } from 'typeorm';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
|
||||
|
@ -28,10 +28,10 @@ import { bindThis } from '@/decorators.js';
|
|||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { isNotNull } from '@/misc/is-not-null.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { MetaService } from '../MetaService.js';
|
||||
import { LdSignatureService } from './LdSignatureService.js';
|
||||
import { ApMfmService } from './ApMfmService.js';
|
||||
import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
|
||||
import { MetaService } from '../MetaService.js';
|
||||
|
||||
@Injectable()
|
||||
export class ApRendererService {
|
||||
|
@ -821,12 +821,12 @@ export class ApRendererService {
|
|||
'_misskey_summary': 'misskey:_misskey_summary',
|
||||
'isCat': 'misskey:isCat',
|
||||
// Firefish
|
||||
firefish: "https://joinfirefish.org/ns#",
|
||||
speakAsCat: "firefish:speakAsCat",
|
||||
firefish: 'https://joinfirefish.org/ns#',
|
||||
speakAsCat: 'firefish:speakAsCat',
|
||||
// Sharkey
|
||||
sharkey: "https://joinsharkey.org/ns#",
|
||||
backgroundUrl: "sharkey:backgroundUrl",
|
||||
listenbrainz: "sharkey:listenbrainz",
|
||||
sharkey: 'https://joinsharkey.org/ns#',
|
||||
backgroundUrl: 'sharkey:backgroundUrl',
|
||||
listenbrainz: 'sharkey:listenbrainz',
|
||||
// vcard
|
||||
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { unique } from '@/misc/prelude/array.js';
|
||||
|
||||
export function extractCustomEmojisFromMfm(nodes: mfm.MfmNode[]): string[] {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { unique } from '@/misc/prelude/array.js';
|
||||
|
||||
export function extractHashtags(nodes: mfm.MfmNode[]): string[] {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// test is located in test/extract-mentions
|
||||
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
|
||||
export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
|
||||
// TODO: 重複を削除
|
||||
|
|
|
@ -353,6 +353,17 @@ export class MiMeta {
|
|||
})
|
||||
public deeplIsPro: boolean;
|
||||
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public deeplFreeMode: boolean;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024,
|
||||
nullable: true,
|
||||
})
|
||||
public deeplFreeInstance: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024,
|
||||
nullable: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as fs from 'node:fs';
|
||||
import * as fsp from 'node:fs/promises';
|
||||
import * as vm from 'node:vm';
|
||||
import * as crypto from 'node:crypto';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
@ -51,7 +52,7 @@ export class ImportNotesProcessorService {
|
|||
|
||||
@bindThis
|
||||
private async uploadFiles(dir: string, user: MiUser, folder?: MiDriveFolder['id']) {
|
||||
const fileList = fs.readdirSync(dir);
|
||||
const fileList = await fsp.readdir(dir);
|
||||
for await (const file of fileList) {
|
||||
const name = `${dir}/${file}`;
|
||||
if (fs.statSync(name).isDirectory()) {
|
||||
|
@ -130,14 +131,16 @@ export class ImportNotesProcessorService {
|
|||
return typeof obj[Symbol.iterator] === 'function';
|
||||
}
|
||||
|
||||
private parseTwitterFile(str : string) : null | [{ tweet: any }] {
|
||||
const removed = str.replace(new RegExp('window\\.YTD\\.tweets\\.part0 = ', 'g'), '');
|
||||
@bindThis
|
||||
private parseTwitterFile(str : string) : { tweet: object }[] {
|
||||
const jsonStr = str.replace(/^\s*window\.YTD\.tweets\.part0\s*=\s*/, '');
|
||||
|
||||
try {
|
||||
return JSON.parse(removed);
|
||||
return JSON.parse(jsonStr);
|
||||
} catch (error) {
|
||||
//The format is not what we expected. Either this file was tampered with or twitters exports changed
|
||||
return null;
|
||||
this.logger.warn('Failed to import twitter notes due to malformed file');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +176,7 @@ export class ImportNotesProcessorService {
|
|||
const destPath = path + '/twitter.zip';
|
||||
|
||||
try {
|
||||
fs.writeFileSync(destPath, '', 'binary');
|
||||
await fsp.writeFile(destPath, '', 'binary');
|
||||
await this.downloadService.downloadUrl(file.url, destPath);
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
|
@ -185,21 +188,13 @@ export class ImportNotesProcessorService {
|
|||
const outputPath = path + '/twitter';
|
||||
try {
|
||||
this.logger.succ(`Unzipping to ${outputPath}`);
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath));
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fsp.readFile(destPath));
|
||||
|
||||
const unprocessedTweetJson = this.parseTwitterFile(fs.readFileSync(outputPath + '/data/tweets.js', 'utf-8'));
|
||||
|
||||
//Make sure that it isnt null (because if something went wrong in parseTwitterFile it returns null)
|
||||
if (unprocessedTweetJson) {
|
||||
const tweets = Object.keys(unprocessedTweetJson).reduce((m, key, i, obj) => {
|
||||
return m.concat(unprocessedTweetJson[i].tweet);
|
||||
}, []);
|
||||
const unprocessedTweets = this.parseTwitterFile(await fsp.readFile(outputPath + '/data/tweets.js', 'utf-8'));
|
||||
|
||||
const tweets = unprocessedTweets.map(e => e.tweet);
|
||||
const processedTweets = await this.recreateChain(['id_str'], ['in_reply_to_status_id_str'], tweets, false);
|
||||
this.queueService.createImportTweetsToDbJob(job.data.user, processedTweets, null);
|
||||
} else {
|
||||
this.logger.warn('Failed to import twitter notes due to malformed file');
|
||||
}
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
|
@ -211,7 +206,7 @@ export class ImportNotesProcessorService {
|
|||
const destPath = path + '/facebook.zip';
|
||||
|
||||
try {
|
||||
fs.writeFileSync(destPath, '', 'binary');
|
||||
await fsp.writeFile(destPath, '', 'binary');
|
||||
await this.downloadService.downloadUrl(file.url, destPath);
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
|
@ -223,8 +218,8 @@ export class ImportNotesProcessorService {
|
|||
const outputPath = path + '/facebook';
|
||||
try {
|
||||
this.logger.succ(`Unzipping to ${outputPath}`);
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath));
|
||||
const postsJson = fs.readFileSync(outputPath + '/your_activity_across_facebook/posts/your_posts__check_ins__photos_and_videos_1.json', 'utf-8');
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fsp.readFile(destPath));
|
||||
const postsJson = await fsp.readFile(outputPath + '/your_activity_across_facebook/posts/your_posts__check_ins__photos_and_videos_1.json', 'utf-8');
|
||||
const posts = JSON.parse(postsJson);
|
||||
const facebookFolder = await this.driveFoldersRepository.findOneBy({ name: 'Facebook', userId: job.data.user.id, parentId: folder?.id });
|
||||
if (facebookFolder == null && folder) {
|
||||
|
@ -244,7 +239,7 @@ export class ImportNotesProcessorService {
|
|||
const destPath = path + '/unknown.zip';
|
||||
|
||||
try {
|
||||
fs.writeFileSync(destPath, '', 'binary');
|
||||
await fsp.writeFile(destPath, '', 'binary');
|
||||
await this.downloadService.downloadUrl(file.url, destPath);
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
|
@ -256,11 +251,11 @@ export class ImportNotesProcessorService {
|
|||
const outputPath = path + '/unknown';
|
||||
try {
|
||||
this.logger.succ(`Unzipping to ${outputPath}`);
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath));
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fsp.readFile(destPath));
|
||||
const isInstagram = type === 'Instagram' || fs.existsSync(outputPath + '/instagram_live') || fs.existsSync(outputPath + '/instagram_ads_and_businesses');
|
||||
const isOutbox = type === 'Mastodon' || fs.existsSync(outputPath + '/outbox.json');
|
||||
if (isInstagram) {
|
||||
const postsJson = fs.readFileSync(outputPath + '/content/posts_1.json', 'utf-8');
|
||||
const postsJson = await fsp.readFile(outputPath + '/content/posts_1.json', 'utf-8');
|
||||
const posts = JSON.parse(postsJson);
|
||||
const igFolder = await this.driveFoldersRepository.findOneBy({ name: 'Instagram', userId: job.data.user.id, parentId: folder?.id });
|
||||
if (igFolder == null && folder) {
|
||||
|
@ -270,16 +265,16 @@ export class ImportNotesProcessorService {
|
|||
}
|
||||
this.queueService.createImportIGToDbJob(job.data.user, posts);
|
||||
} else if (isOutbox) {
|
||||
const actorJson = fs.readFileSync(outputPath + '/actor.json', 'utf-8');
|
||||
const actorJson = await fsp.readFile(outputPath + '/actor.json', 'utf-8');
|
||||
const actor = JSON.parse(actorJson);
|
||||
const isPleroma = actor['@context'].some((v: any) => typeof v === 'string' && v.match(/litepub(.*)/));
|
||||
if (isPleroma) {
|
||||
const outboxJson = fs.readFileSync(outputPath + '/outbox.json', 'utf-8');
|
||||
const outboxJson = await fsp.readFile(outputPath + '/outbox.json', 'utf-8');
|
||||
const outbox = JSON.parse(outboxJson);
|
||||
const processedToots = await this.recreateChain(['object', 'id'], ['object', 'inReplyTo'], outbox.orderedItems.filter((x: any) => x.type === 'Create' && x.object.type === 'Note'), true);
|
||||
this.queueService.createImportPleroToDbJob(job.data.user, processedToots, null);
|
||||
} else {
|
||||
const outboxJson = fs.readFileSync(outputPath + '/outbox.json', 'utf-8');
|
||||
const outboxJson = await fsp.readFile(outputPath + '/outbox.json', 'utf-8');
|
||||
const outbox = JSON.parse(outboxJson);
|
||||
let mastoFolder = await this.driveFoldersRepository.findOneBy({ name: 'Mastodon', userId: job.data.user.id, parentId: folder?.id });
|
||||
if (mastoFolder == null && folder) {
|
||||
|
@ -302,7 +297,7 @@ export class ImportNotesProcessorService {
|
|||
this.logger.info(`Temp dir is ${path}`);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(path, '', 'utf-8');
|
||||
await fsp.writeFile(path, '', 'utf-8');
|
||||
await this.downloadService.downloadUrl(file.url, path);
|
||||
} catch (e) { // TODO: 何度か再試行
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
|
@ -311,7 +306,7 @@ export class ImportNotesProcessorService {
|
|||
throw e;
|
||||
}
|
||||
|
||||
const notesJson = fs.readFileSync(path, 'utf-8');
|
||||
const notesJson = await fsp.readFile(path, 'utf-8');
|
||||
const notes = JSON.parse(notesJson);
|
||||
const processedNotes = await this.recreateChain(['id'], ['replyId'], notes, false);
|
||||
this.queueService.createImportKeyNotesToDbJob(job.data.user, processedNotes, null);
|
||||
|
@ -590,7 +585,8 @@ export class ImportNotesProcessorService {
|
|||
|
||||
try {
|
||||
const date = new Date(tweet.created_at);
|
||||
const textReplaceURLs = tweet.entities.urls && tweet.entities.urls.length > 0 ? await replaceTwitterUrls(tweet.full_text, tweet.entities.urls) : tweet.full_text;
|
||||
const decodedText = tweet.full_text.replaceAll('>', '>').replaceAll('<', '<').replaceAll('&', '&');
|
||||
const textReplaceURLs = tweet.entities.urls && tweet.entities.urls.length > 0 ? await replaceTwitterUrls(decodedText, tweet.entities.urls) : decodedText;
|
||||
const text = tweet.entities.user_mentions && tweet.entities.user_mentions.length > 0 ? await replaceTwitterMentions(textReplaceURLs, tweet.entities.user_mentions) : textReplaceURLs;
|
||||
const files: MiDriveFile[] = [];
|
||||
|
||||
|
|
|
@ -395,6 +395,14 @@ export const meta = {
|
|||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
deeplFreeMode: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
deeplFreeInstance: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
defaultDarkTheme: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
|
@ -576,6 +584,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle,
|
||||
deeplAuthKey: instance.deeplAuthKey,
|
||||
deeplIsPro: instance.deeplIsPro,
|
||||
deeplFreeMode: instance.deeplFreeMode,
|
||||
deeplFreeInstance: instance.deeplFreeInstance,
|
||||
enableIpLogging: instance.enableIpLogging,
|
||||
enableActiveEmailValidation: instance.enableActiveEmailValidation,
|
||||
enableVerifymailApi: instance.enableVerifymailApi,
|
||||
|
|
|
@ -91,6 +91,8 @@ export const paramDef = {
|
|||
summalyProxy: { type: 'string', nullable: true },
|
||||
deeplAuthKey: { type: 'string', nullable: true },
|
||||
deeplIsPro: { type: 'boolean' },
|
||||
deeplFreeMode: { type: 'boolean' },
|
||||
deeplFreeInstance: { type: 'string', nullable: true },
|
||||
enableEmail: { type: 'boolean' },
|
||||
email: { type: 'string', nullable: true },
|
||||
smtpSecure: { type: 'boolean' },
|
||||
|
@ -479,6 +481,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
set.deeplIsPro = ps.deeplIsPro;
|
||||
}
|
||||
|
||||
if (ps.deeplFreeMode !== undefined) {
|
||||
set.deeplFreeMode = ps.deeplFreeMode;
|
||||
}
|
||||
|
||||
if (ps.deeplFreeInstance !== undefined) {
|
||||
if (ps.deeplFreeInstance === '') {
|
||||
set.deeplFreeInstance = null;
|
||||
} else {
|
||||
set.deeplFreeInstance = ps.deeplFreeInstance;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps.enableIpLogging !== undefined) {
|
||||
set.enableIpLogging = ps.enableIpLogging;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import RE2 from 're2';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import ms from 'ms';
|
||||
import { JSDOM } from 'jsdom';
|
||||
|
|
|
@ -411,7 +411,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
enableEmail: instance.enableEmail,
|
||||
enableServiceWorker: instance.enableServiceWorker,
|
||||
|
||||
translatorAvailable: instance.deeplAuthKey != null,
|
||||
translatorAvailable: instance.deeplAuthKey != null || instance.deeplFreeMode && instance.deeplFreeInstance,
|
||||
|
||||
serverRules: instance.serverRules,
|
||||
|
||||
|
|
|
@ -81,19 +81,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
const instance = await this.metaService.fetch();
|
||||
|
||||
if (instance.deeplAuthKey == null) {
|
||||
if (instance.deeplAuthKey == null && !instance.deeplFreeMode) {
|
||||
return 204; // TODO: 良い感じのエラー返す
|
||||
}
|
||||
|
||||
if (instance.deeplFreeMode && !instance.deeplFreeInstance) {
|
||||
return 204;
|
||||
}
|
||||
|
||||
let targetLang = ps.targetLang;
|
||||
if (targetLang.includes('-')) targetLang = targetLang.split('-')[0];
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('auth_key', instance.deeplAuthKey);
|
||||
if (instance.deeplAuthKey) params.append('auth_key', instance.deeplAuthKey);
|
||||
params.append('text', note.text);
|
||||
params.append('target_lang', targetLang);
|
||||
|
||||
const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate';
|
||||
const endpoint = instance.deeplFreeMode && instance.deeplFreeInstance ? instance.deeplFreeInstance : instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate';
|
||||
|
||||
const res = await this.httpRequestService.send(endpoint, {
|
||||
method: 'POST',
|
||||
|
@ -103,7 +107,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
},
|
||||
body: params.toString(),
|
||||
});
|
||||
|
||||
if (instance.deeplAuthKey) {
|
||||
const json = (await res.json()) as {
|
||||
translations: {
|
||||
detected_source_language: string;
|
||||
|
@ -115,6 +119,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
sourceLang: json.translations[0].detected_source_language,
|
||||
text: json.translations[0].text,
|
||||
};
|
||||
} else {
|
||||
const json = (await res.json()) as {
|
||||
code: number,
|
||||
message: string,
|
||||
data: string,
|
||||
source_lang: string,
|
||||
target_lang: string,
|
||||
alternatives: string[],
|
||||
};
|
||||
|
||||
const languageNames = new Intl.DisplayNames(['en'], {
|
||||
type: 'language',
|
||||
});
|
||||
|
||||
return {
|
||||
sourceLang: languageNames.of(json.source_lang),
|
||||
text: json.data,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Entity } from 'megalodon';
|
||||
import mfm from '@sharkey/sfm-js';
|
||||
import mfm from '@transfem-org/sfm-js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MfmService } from '@/core/MfmService.js';
|
||||
import type { Config } from '@/config.js';
|
||||
|
@ -9,9 +9,9 @@ import type { MiUser } from '@/models/User.js';
|
|||
import type { NoteEditRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { GetterService } from '../GetterService.js';
|
||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GetterService } from '../GetterService.js';
|
||||
|
||||
export enum IdConvertType {
|
||||
MastodonId,
|
||||
|
@ -94,10 +94,10 @@ export class MastoConverters {
|
|||
text_url: f.url,
|
||||
meta: {
|
||||
width: f.properties.width,
|
||||
height: f.properties.height
|
||||
height: f.properties.height,
|
||||
},
|
||||
description: f.comment ? f.comment : null,
|
||||
blurhash: f.blurhash ? f.blurhash : null
|
||||
blurhash: f.blurhash ? f.blurhash : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ export class MastoConverters {
|
|||
sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false),
|
||||
spoiler_text: edit.cw ?? '',
|
||||
poll: null,
|
||||
media_attachments: files.then(files => files.length > 0 ? files.map((f) => this.encodeFile(f)) : [])
|
||||
media_attachments: files.then(files => files.length > 0 ? files.map((f) => this.encodeFile(f)) : []),
|
||||
};
|
||||
lastDate = edit.updatedAt;
|
||||
history.push(awaitAll(item));
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { CoreModule } from '@/core/CoreModule.js';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { parse } from '@sharkey/sfm-js';
|
||||
import { parse } from '@transfem-org/sfm-js';
|
||||
import { extractMentions } from '@/misc/extract-mentions.js';
|
||||
|
||||
describe('Extract mentions', () => {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-replace": "5.0.5",
|
||||
"@rollup/pluginutils": "5.1.0",
|
||||
"@sharkey/sfm-js": "0.24.4",
|
||||
"@transfem-org/sfm-js": "0.24.4",
|
||||
"@syuilo/aiscript": "0.17.0",
|
||||
"@phosphor-icons/web": "^2.0.3",
|
||||
"@twemoji/parser": "15.0.0",
|
||||
|
|
|
@ -58,6 +58,21 @@ watch(() => props.lang, (to) => {
|
|||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.codeBlockRoot :deep(.shiki) > code {
|
||||
counter-reset: step;
|
||||
counter-increment: step 0;
|
||||
}
|
||||
|
||||
.codeBlockRoot :deep(.shiki) > code > .line::before {
|
||||
content: counter(step);
|
||||
counter-increment: step;
|
||||
width: 1rem;
|
||||
margin-right: 1.5rem;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
color: rgba(115,138,148,.4)
|
||||
}
|
||||
|
||||
.codeBlockRoot :deep(.shiki) {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
|
|
|
@ -204,6 +204,8 @@ watch(v, newValue => {
|
|||
min-width: calc(100% - 24px);
|
||||
height: 100%;
|
||||
padding: 12px;
|
||||
// the +2.5 rem is because of the line numbers
|
||||
padding-left: calc(12px + 2.5rem);
|
||||
line-height: 1.5em;
|
||||
font-size: 1em;
|
||||
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
|
||||
|
|
|
@ -178,7 +178,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
|
@ -477,7 +477,7 @@ function renote(visibility: Visibility | 'local') {
|
|||
renoted.value = true;
|
||||
});
|
||||
}
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) {
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) {
|
||||
const el = renoteButton.value as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
|
|
@ -170,7 +170,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-if="!repliesLoaded" style="padding: 16px">
|
||||
<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
|
||||
</div>
|
||||
<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" />
|
||||
<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/>
|
||||
</div>
|
||||
<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
|
||||
<MkPagination :pagination="renotesPagination" :disableAutoLoad="true">
|
||||
|
@ -221,7 +221,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
|
@ -501,7 +501,7 @@ function renote(visibility: Visibility | 'local') {
|
|||
os.toast(i18n.ts.renoted);
|
||||
renoted.value = true;
|
||||
});
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) {
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) {
|
||||
const el = renoteButton.value as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
|
|
@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import { toASCII } from 'punycode/';
|
||||
|
|
|
@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import MkMediaList from '@/components/MkMediaList.vue';
|
||||
import MkPoll from '@/components/MkPoll.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
|
|
@ -180,7 +180,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import SkNoteSub from '@/components/SkNoteSub.vue';
|
||||
import SkNoteHeader from '@/components/SkNoteHeader.vue';
|
||||
|
@ -478,7 +478,7 @@ function renote(visibility: Visibility | 'local') {
|
|||
renoted.value = true;
|
||||
});
|
||||
}
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) {
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) {
|
||||
const el = renoteButton.value as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
|
|
@ -229,7 +229,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, onMounted, onUnmounted, onUpdated, provide, ref, shallowRef, watch } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import SkNoteSub from '@/components/SkNoteSub.vue';
|
||||
import SkNoteSimple from '@/components/SkNoteSimple.vue';
|
||||
|
@ -510,7 +510,7 @@ function renote(visibility: Visibility | 'local') {
|
|||
os.toast(i18n.ts.renoted);
|
||||
renoted.value = true;
|
||||
});
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel?.allowRenoteToExternal) {
|
||||
} else if (!appearNote.value.channel || appearNote.value.channel.allowRenoteToExternal) {
|
||||
const el = renoteButton.value as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted, ref, shallowRef, computed } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import MkMediaList from '@/components/MkMediaList.vue';
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import { VNode, h, defineAsyncComponent, SetupContext } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkUrl from '@/components/global/MkUrl.vue';
|
||||
import MkTime from '@/components/global/MkTime.vue';
|
||||
|
@ -52,7 +52,7 @@ type MfmEvents = {
|
|||
// eslint-disable-next-line import/no-default-export
|
||||
export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
|
||||
const isNote = props.isNote ?? true;
|
||||
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat ? props.author?.speakAsCat : false : false : false;
|
||||
const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat ? props.author.speakAsCat : false : false : false;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (props.text == null || props.text === '') return;
|
||||
|
|
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { TextBlock } from './block.type.js';
|
||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
|
||||
|
|
|
@ -19,6 +19,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkSwitch v-model="deeplIsPro">
|
||||
<template #label>Pro account</template>
|
||||
</MkSwitch>
|
||||
<MkSwitch v-model="deeplFreeMode">
|
||||
<template #label>{{ i18n.ts.deeplFreeMode }}</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-if="deeplFreeMode" v-model="deeplFreeInstance" :placeholder="'example.com/translate'">
|
||||
<template #prefix><i class="ph-globe-simple ph-bold ph-lg"></i></template>
|
||||
<template #label>DeepLX-JS URL</template>
|
||||
<template #caption>{{ i18n.ts.deeplFreeModeDescription }}</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</FormSection>
|
||||
</FormSuspense>
|
||||
|
@ -49,17 +57,23 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|||
|
||||
const deeplAuthKey = ref<string>('');
|
||||
const deeplIsPro = ref<boolean>(false);
|
||||
const deeplFreeMode = ref<boolean>(false);
|
||||
const deeplFreeInstance = ref<string>('');
|
||||
|
||||
async function init() {
|
||||
const meta = await misskeyApi('admin/meta');
|
||||
deeplAuthKey.value = meta.deeplAuthKey;
|
||||
deeplIsPro.value = meta.deeplIsPro;
|
||||
deeplFreeMode.value = meta.deeplFreeMode;
|
||||
deeplFreeInstance.value = meta.deeplFreeInstance;
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
deeplAuthKey: deeplAuthKey.value,
|
||||
deeplIsPro: deeplIsPro.value,
|
||||
deeplFreeMode: deeplFreeMode.value,
|
||||
deeplFreeInstance: deeplFreeInstance.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
|
||||
export function checkAnimationFromMfm(nodes: mfm.MfmNode[]): boolean {
|
||||
const animatedNodes = mfm.extract(nodes, (node) => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// test is located in test/extract-mentions
|
||||
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
|
||||
export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
|
||||
// TODO: 重複を削除
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as mfm from '@sharkey/sfm-js';
|
||||
import * as mfm from '@transfem-org/sfm-js';
|
||||
import { unique } from '@/scripts/array.js';
|
||||
|
||||
// unique without hash
|
||||
|
|
|
@ -64,8 +64,12 @@ export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta>
|
|||
try {
|
||||
ast = parser.parse(code);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
throw new Error(`Aiscript syntax error\n${(err as Error).message}`);
|
||||
} else {
|
||||
throw new Error('Aiscript syntax error');
|
||||
}
|
||||
}
|
||||
|
||||
const meta = Interpreter.collectMetadata(ast);
|
||||
if (meta == null) {
|
||||
|
|
|
@ -85,6 +85,9 @@ type AdminAnnouncementsListResponse = operations['admin/announcements/list']['re
|
|||
// @public (undocumented)
|
||||
type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminApproveUserRequest = operations['admin/approve-user']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -208,6 +211,9 @@ type AdminInviteListResponse = operations['admin/invite/list']['responses']['200
|
|||
// @public (undocumented)
|
||||
type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminNsfwUserRequest = operations['admin/nsfw-user']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -304,15 +310,24 @@ type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['cont
|
|||
// @public (undocumented)
|
||||
type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminSilenceUserRequest = operations['admin/silence-user']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminUnnsfwUserRequest = operations['admin/unnsfw-user']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminUnsilenceUserRequest = operations['admin/unsilence-user']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -594,6 +609,13 @@ export type Channels = {
|
|||
};
|
||||
receives: null;
|
||||
};
|
||||
bubbleTimeline: {
|
||||
params: null;
|
||||
events: {
|
||||
note: (payload: Note) => void;
|
||||
};
|
||||
receives: null;
|
||||
};
|
||||
userList: {
|
||||
params: {
|
||||
listId: string;
|
||||
|
@ -1165,7 +1187,12 @@ declare namespace entities {
|
|||
AdminShowUserResponse,
|
||||
AdminShowUsersRequest,
|
||||
AdminShowUsersResponse,
|
||||
AdminNsfwUserRequest,
|
||||
AdminUnnsfwUserRequest,
|
||||
AdminSilenceUserRequest,
|
||||
AdminUnsilenceUserRequest,
|
||||
AdminSuspendUserRequest,
|
||||
AdminApproveUserRequest,
|
||||
AdminUnsuspendUserRequest,
|
||||
AdminUpdateMetaRequest,
|
||||
AdminDeleteAccountRequest,
|
||||
|
@ -1393,6 +1420,7 @@ declare namespace entities {
|
|||
IGalleryPostsResponse,
|
||||
IImportBlockingRequest,
|
||||
IImportFollowingRequest,
|
||||
IImportNotesRequest,
|
||||
IImportMutingRequest,
|
||||
IImportUserListsRequest,
|
||||
IImportAntennasRequest,
|
||||
|
@ -1410,6 +1438,7 @@ declare namespace entities {
|
|||
IRegenerateTokenRequest,
|
||||
IRegistryGetAllRequest,
|
||||
IRegistryGetAllResponse,
|
||||
IRegistryGetUnsecureRequest,
|
||||
IRegistryGetDetailRequest,
|
||||
IRegistryGetDetailResponse,
|
||||
IRegistryGetRequest,
|
||||
|
@ -1477,6 +1506,8 @@ declare namespace entities {
|
|||
NotesFeaturedResponse,
|
||||
NotesGlobalTimelineRequest,
|
||||
NotesGlobalTimelineResponse,
|
||||
NotesBubbleTimelineRequest,
|
||||
NotesBubbleTimelineResponse,
|
||||
NotesHybridTimelineRequest,
|
||||
NotesHybridTimelineResponse,
|
||||
NotesLocalTimelineRequest,
|
||||
|
@ -1490,6 +1521,7 @@ declare namespace entities {
|
|||
NotesReactionsResponse,
|
||||
NotesReactionsCreateRequest,
|
||||
NotesReactionsDeleteRequest,
|
||||
NotesLikeRequest,
|
||||
NotesRenotesRequest,
|
||||
NotesRenotesResponse,
|
||||
NotesRepliesRequest,
|
||||
|
@ -1511,6 +1543,10 @@ declare namespace entities {
|
|||
NotesUnrenoteRequest,
|
||||
NotesUserListTimelineRequest,
|
||||
NotesUserListTimelineResponse,
|
||||
NotesEditRequest,
|
||||
NotesEditResponse,
|
||||
NotesVersionsRequest,
|
||||
NotesVersionsResponse,
|
||||
NotificationsCreateRequest,
|
||||
PagePushRequest,
|
||||
PagesCreateRequest,
|
||||
|
@ -1619,6 +1655,7 @@ declare namespace entities {
|
|||
FetchExternalResourcesRequest,
|
||||
FetchExternalResourcesResponse,
|
||||
RetentionResponse,
|
||||
SponsorsRequest,
|
||||
BubbleGameRegisterRequest,
|
||||
BubbleGameRegisterResponse,
|
||||
BubbleGameRankingRequest,
|
||||
|
@ -2016,6 +2053,9 @@ type IImportFollowingRequest = operations['i/import-following']['requestBody']['
|
|||
// @public (undocumented)
|
||||
type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportNotesRequest = operations['i/import-notes']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -2097,6 +2137,9 @@ type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content'
|
|||
// @public (undocumented)
|
||||
type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IRegistryGetUnsecureRequest = operations['i/registry/get-unsecure']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -2196,6 +2239,9 @@ type ModerationLog = {
|
|||
} & ({
|
||||
type: 'updateServerSettings';
|
||||
info: ModerationLogPayloads['updateServerSettings'];
|
||||
} | {
|
||||
type: 'approve';
|
||||
info: ModerationLogPayloads['approve'];
|
||||
} | {
|
||||
type: 'suspend';
|
||||
info: ModerationLogPayloads['suspend'];
|
||||
|
@ -2307,7 +2353,7 @@ type ModerationLog = {
|
|||
});
|
||||
|
||||
// @public (undocumented)
|
||||
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
|
||||
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "approve", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
|
||||
|
||||
// @public (undocumented)
|
||||
type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
|
||||
|
@ -2342,6 +2388,12 @@ type NoteFavorite = components['schemas']['NoteFavorite'];
|
|||
// @public (undocumented)
|
||||
type NoteReaction = components['schemas']['NoteReaction'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesBubbleTimelineRequest = operations['notes/bubble-timeline']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesBubbleTimelineResponse = operations['notes/bubble-timeline']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -2369,6 +2421,12 @@ type NotesCreateResponse = operations['notes/create']['responses']['200']['conte
|
|||
// @public (undocumented)
|
||||
type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesEditRequest = operations['notes/edit']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesEditResponse = operations['notes/edit']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -2393,6 +2451,9 @@ type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBo
|
|||
// @public (undocumented)
|
||||
type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesLikeRequest = operations['notes/like']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -2495,6 +2556,12 @@ type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requ
|
|||
// @public (undocumented)
|
||||
type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesVersionsRequest = operations['notes/versions']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type NotesVersionsResponse = operations['notes/versions']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
export const noteVisibilities: readonly ["public", "home", "followers", "specified"];
|
||||
|
||||
|
@ -2553,7 +2620,7 @@ type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['
|
|||
function parse(acct: string): Acct;
|
||||
|
||||
// @public (undocumented)
|
||||
export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
|
||||
export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:approve-user", "write:admin:nsfw-user", "write:admin:unnsfw-user", "write:admin:silence-user", "write:admin:unsilence-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
|
||||
|
||||
// @public (undocumented)
|
||||
type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
|
||||
|
@ -2746,6 +2813,9 @@ type SignupResponse = MeDetailed & {
|
|||
token: string;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
type SponsorsRequest = operations['sponsors']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* version: 2024.2.0-beta.6
|
||||
* generatedAt: 2024-01-24T07:32:10.455Z
|
||||
* version: 2024.2.0-beta2
|
||||
* generatedAt: 2024-01-26T20:30:18.423Z
|
||||
*/
|
||||
|
||||
import type { SwitchCaseResponseType } from '../api.js';
|
||||
|
@ -691,6 +691,50 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:nsfw-user*
|
||||
*/
|
||||
request<E extends 'admin/nsfw-user', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:unnsfw-user*
|
||||
*/
|
||||
request<E extends 'admin/unnsfw-user', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:silence-user*
|
||||
*/
|
||||
request<E extends 'admin/silence-user', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:unsilence-user*
|
||||
*/
|
||||
request<E extends 'admin/unsilence-user', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
|
@ -702,6 +746,17 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:approve-user*
|
||||
*/
|
||||
request<E extends 'admin/approve-user', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
|
@ -2514,6 +2569,17 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *read:account*
|
||||
*/
|
||||
request<E extends 'i/registry/get-unsecure', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
|
@ -2993,6 +3059,17 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *No*
|
||||
*/
|
||||
request<E extends 'notes/bubble-timeline', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
|
@ -4054,7 +4131,8 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/** No description provided.
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* version: 2024.2.0-beta.6
|
||||
* generatedAt: 2024-01-24T07:32:10.453Z
|
||||
* version: 2024.2.0-beta2
|
||||
* generatedAt: 2024-01-26T20:30:18.421Z
|
||||
*/
|
||||
|
||||
import type {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* version: 2024.2.0-beta.6
|
||||
* generatedAt: 2024-01-24T07:32:10.452Z
|
||||
* version: 2024.2.0-beta2
|
||||
* generatedAt: 2024-01-26T20:30:18.419Z
|
||||
*/
|
||||
|
||||
import { operations } from './types.js';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* version: 2024.2.0-beta.6
|
||||
* generatedAt: 2024-01-24T07:32:10.450Z
|
||||
* version: 2024.2.0-beta2
|
||||
* generatedAt: 2024-01-26T20:30:18.418Z
|
||||
*/
|
||||
|
||||
import { components } from './types.js';
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
||||
|
||||
/*
|
||||
* version: 2024.2.0-beta.6
|
||||
* generatedAt: 2024-01-24T07:32:10.370Z
|
||||
* version: 2024.2.0-beta2
|
||||
* generatedAt: 2024-01-26T20:30:18.319Z
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -4784,6 +4784,8 @@ export type operations = {
|
|||
backgroundImageUrl: string | null;
|
||||
deeplAuthKey: string | null;
|
||||
deeplIsPro: boolean;
|
||||
deeplFreeMode: boolean;
|
||||
deeplFreeInstance: string | null;
|
||||
defaultDarkTheme: string | null;
|
||||
defaultLightTheme: string | null;
|
||||
description: string | null;
|
||||
|
@ -8795,6 +8797,8 @@ export type operations = {
|
|||
summalyProxy?: string | null;
|
||||
deeplAuthKey?: string | null;
|
||||
deeplIsPro?: boolean;
|
||||
deeplFreeMode?: boolean;
|
||||
deeplFreeInstance?: string | null;
|
||||
enableEmail?: boolean;
|
||||
email?: string | null;
|
||||
smtpSecure?: boolean;
|
||||
|
@ -14101,6 +14105,7 @@ export type operations = {
|
|||
subscribing?: boolean | null;
|
||||
publishing?: boolean | null;
|
||||
nsfw?: boolean | null;
|
||||
bubble?: boolean | null;
|
||||
/** @default 30 */
|
||||
limit?: number;
|
||||
/** @default 0 */
|
||||
|
@ -26440,9 +26445,40 @@ export type operations = {
|
|||
204: {
|
||||
content: never;
|
||||
};
|
||||
/** @description Client error */
|
||||
400: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** bubble-game/register
|
||||
/** @description Authentication error */
|
||||
401: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden error */
|
||||
403: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description I'm Ai */
|
||||
418: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Internal server error */
|
||||
500: {
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* bubble-game/register
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||
|
|
|
@ -115,9 +115,6 @@ importers:
|
|||
'@peertube/http-signature':
|
||||
specifier: 1.7.0
|
||||
version: 1.7.0
|
||||
'@sharkey/sfm-js':
|
||||
specifier: 0.24.4
|
||||
version: 0.24.4
|
||||
'@simplewebauthn/server':
|
||||
specifier: 9.0.0
|
||||
version: 9.0.0
|
||||
|
@ -133,6 +130,9 @@ importers:
|
|||
'@swc/core':
|
||||
specifier: 1.3.105
|
||||
version: 1.3.105
|
||||
'@transfem-org/sfm-js':
|
||||
specifier: 0.24.4
|
||||
version: 0.24.4
|
||||
'@twemoji/parser':
|
||||
specifier: 15.0.0
|
||||
version: 15.0.0
|
||||
|
@ -696,12 +696,12 @@ importers:
|
|||
'@rollup/pluginutils':
|
||||
specifier: 5.1.0
|
||||
version: 5.1.0(rollup@4.9.6)
|
||||
'@sharkey/sfm-js':
|
||||
specifier: 0.24.4
|
||||
version: 0.24.4
|
||||
'@syuilo/aiscript':
|
||||
specifier: 0.17.0
|
||||
version: 0.17.0
|
||||
'@transfem-org/sfm-js':
|
||||
specifier: 0.24.4
|
||||
version: 0.24.4
|
||||
'@twemoji/parser':
|
||||
specifier: 15.0.0
|
||||
version: 15.0.0
|
||||
|
@ -5782,12 +5782,6 @@ packages:
|
|||
string-argv: 0.3.1
|
||||
dev: true
|
||||
|
||||
/@sharkey/sfm-js@0.24.4:
|
||||
resolution: {integrity: sha512-m9reKRceS8kmFEPFlK2nhbN6mP9kE1I895WAc9OlFISMdT5L+IdvBW1NHv8dzjSPSTFZLeCfMHbys1oz+5uLuA==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.4/sfm-js-0.24.4.tgz}
|
||||
dependencies:
|
||||
'@twemoji/parser': 15.0.0
|
||||
dev: false
|
||||
|
||||
/@sideway/address@4.1.4:
|
||||
resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
|
||||
dependencies:
|
||||
|
@ -7513,6 +7507,12 @@ packages:
|
|||
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||
dev: false
|
||||
|
||||
/@transfem-org/sfm-js@0.24.4:
|
||||
resolution: {integrity: sha1-0wEXqL5UJseGFO4GGFRrES6NCDk=, tarball: https://activitypub.software/api/v4/projects/2/packages/npm/@transfem-org/sfm-js/-/@transfem-org/sfm-js-0.24.4.tgz}
|
||||
dependencies:
|
||||
'@twemoji/parser': 15.0.0
|
||||
dev: false
|
||||
|
||||
/@trysound/sax@0.2.0:
|
||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
|
Loading…
Reference in a new issue