Merge pull request '[Chore] Partial translating of ActivityPub/Boot code + Formatting' (#9229) from prettykool/calckey:develop into develop

Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9229
This commit is contained in:
Cleo 2022-12-18 21:01:22 +00:00
commit b1933d00b9
21 changed files with 132 additions and 129 deletions

View File

@ -32,8 +32,8 @@ export default async function() {
await workerMain(); await workerMain();
} }
// ユニットテスト時にMisskeyが子プロセスで起動された時のため // For when Calckey is started in a child process during unit testing.
// それ以外のときは process.send は使えないので弾く // Otherwise, process.send cannot be used, so start it.
if (process.send) { if (process.send) {
process.send('ok'); process.send('ok');
} }

View File

@ -19,7 +19,7 @@ export class Notification {
public createdAt: Date; public createdAt: Date;
/** /**
* * Notification Recipient ID
*/ */
@Index() @Index()
@Column({ @Column({
@ -35,7 +35,7 @@ export class Notification {
public notifiee: User | null; public notifiee: User | null;
/** /**
* (initiator) * Notification sender (initiator)
*/ */
@Index() @Index()
@Column({ @Column({
@ -52,19 +52,19 @@ export class Notification {
public notifier: User | null; public notifier: User | null;
/** /**
* * Notification types:
* follow - * follow - Follow request
* mention - 稿 * mention - User was referenced in a post.
* reply - (Watchしている)稿 * reply - A post that a user made (or was watching) has been replied to.
* renote - (Watchしている)稿Renoteされた * renote - A post that a user made (or was watching) has been renoted.
* quote - (Watchしている)稿Renoteされた * quote - A post that a user made (or was watching) has been quoted and renoted.
* reaction - (Watchしている)稿 * reaction - (Watchしている)稿
* pollVote - (Watchしている)稿 * pollVote - (Watchしている)稿
* pollEnded - * pollEnded -
* receiveFollowRequest - * receiveFollowRequest -
* followRequestAccepted - * followRequestAccepted - A follow request has been accepted.
* groupInvited - * groupInvited -
* app - * app - App notifications.
*/ */
@Index() @Index()
@Column('enum', { @Column('enum', {
@ -74,12 +74,12 @@ export class Notification {
public type: typeof notificationTypes[number]; public type: typeof notificationTypes[number];
/** /**
* * Whether the notification was read.
*/ */
@Index() @Index()
@Column('boolean', { @Column('boolean', {
default: false, default: false,
comment: 'Whether the Notification is read.', comment: 'Whether the notification was read.',
}) })
public isRead: boolean; public isRead: boolean;
@ -130,7 +130,7 @@ export class Notification {
public choice: number | null; public choice: number | null;
/** /**
* body * App notification body
*/ */
@Column('varchar', { @Column('varchar', {
length: 2048, nullable: true, length: 2048, nullable: true,
@ -138,8 +138,8 @@ export class Notification {
public customBody: string | null; public customBody: string | null;
/** /**
* header * App notification header
* () * (If omitted, it is expected to be displayed with the app name)
*/ */
@Column('varchar', { @Column('varchar', {
length: 256, nullable: true, length: 256, nullable: true,
@ -147,8 +147,8 @@ export class Notification {
public customHeader: string | null; public customHeader: string | null;
/** /**
* icon(URL) * App notification icon (URL)
* () * (If omitted, it is expected to be displayed as an app icon)
*/ */
@Column('varchar', { @Column('varchar', {
length: 1024, nullable: true, length: 1024, nullable: true,
@ -156,7 +156,7 @@ export class Notification {
public customIcon: string | null; public customIcon: string | null;
/** /**
* () * App notification app (token for)
*/ */
@Index() @Index()
@Column({ @Column({

View File

@ -14,7 +14,7 @@ import { Notes } from '@/models/index.js';
const logger = apLogger; const logger = apLogger;
/** /**
* * Handle announcement activities
*/ */
export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
const uri = getApId(activity); const uri = getApId(activity);
@ -23,25 +23,25 @@ export default async function(resolver: Resolver, actor: CacheableRemoteUser, ac
return; return;
} }
// アナウンス先をブロックしてたら中断 // Interrupt if you block the announcement destination
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) return; if (meta.blockedHosts.includes(extractDbHost(uri))) return;
const unlock = await getApLock(uri); const unlock = await getApLock(uri);
try { try {
// 既に同じURIを持つものが登録されていないかチェック // Check if something with the same URI is already registered
const exist = await fetchNote(uri); const exist = await fetchNote(uri);
if (exist) { if (exist) {
return; return;
} }
// Announce対象をresolve // Resolve Announce target
let renote; let renote;
try { try {
renote = await resolveNote(targetUri); renote = await resolveNote(targetUri);
} catch (e) { } catch (e) {
// 対象が4xxならスキップ // Skip if target is 4xx
if (e instanceof StatusError) { if (e instanceof StatusError) {
if (e.isClientError) { if (e.isClientError) {
logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`);

View File

@ -7,7 +7,7 @@ import { extractDbHost } from '@/misc/convert-host.js';
import { StatusError } from '@/misc/fetch.js'; import { StatusError } from '@/misc/fetch.js';
/** /**
* 稿 * Handle post creation activity
*/ */
export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
const uri = getApId(note); const uri = getApId(note);

View File

@ -5,18 +5,19 @@ import { toSingle } from '@/prelude/array.js';
import { deleteActor } from './actor.js'; import { deleteActor } from './actor.js';
/** /**
* * Handle delete activity
*/ */
export default async (actor: CacheableRemoteUser, activity: IDelete): Promise<string> => { export default async (actor: CacheableRemoteUser, activity: IDelete): Promise<string> => {
if ('actor' in activity && actor.uri !== activity.actor) { if ('actor' in activity && actor.uri !== activity.actor) {
throw new Error('invalid actor'); throw new Error('invalid actor');
} }
// 削除対象objectのtype // Type of object to be deleted
let formerType: string | undefined; let formerType: string | undefined;
if (typeof activity.object === 'string') { if (typeof activity.object === 'string') {
// typeが不明だけど、どうせ消えてるのでremote resolveしない // The type is unknown, but it has disappeared
// anyway, so it does not remote resolve
formerType = undefined; formerType = undefined;
} else { } else {
const object = activity.object as IObject; const object = activity.object as IObject;
@ -29,12 +30,13 @@ export default async (actor: CacheableRemoteUser, activity: IDelete): Promise<st
const uri = getApId(activity.object); const uri = getApId(activity.object);
// type不明でもactorとobjectが同じならばそれはPersonに違いない // Even if type is unknown, if actor and object are the same,
// it must be `Person`.
if (!formerType && actor.uri === uri) { if (!formerType && actor.uri === uri) {
formerType = 'Person'; formerType = 'Person';
} }
// それでもなかったらおそらくNote // If not, fallback to `Note`.
if (!formerType) { if (!formerType) {
formerType = 'Note'; formerType = 'Note';
} }

View File

@ -21,7 +21,7 @@ export default async function(actor: CacheableRemoteUser, uri: string): Promise<
if (message == null) return 'message not found'; if (message == null) return 'message not found';
if (message.userId !== actor.id) { if (message.userId !== actor.id) {
return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; return 'The user trying to delete the post is not the post author';
} }
await deleteMessage(message); await deleteMessage(message);
@ -30,7 +30,7 @@ export default async function(actor: CacheableRemoteUser, uri: string): Promise<
} }
if (note.userId !== actor.id) { if (note.userId !== actor.id) {
return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; return 'The user trying to delete the post is not the post author';
} }
await deleteNode(actor, note); await deleteNode(actor, note);

View File

@ -6,8 +6,9 @@ import { In } from 'typeorm';
import { genId } from '@/misc/gen-id.js'; import { genId } from '@/misc/gen-id.js';
export default async (actor: CacheableRemoteUser, activity: IFlag): Promise<string> => { export default async (actor: CacheableRemoteUser, activity: IFlag): Promise<string> => {
// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので // The object is `(User | Note) | (User | Note) []`, but it cannot be
// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する // matched with all patterns of the DB schema, so the target user is the first
// user and it is stored as a comment.
const uris = getApIds(activity.object); const uris = getApIds(activity.object);
const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!); const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!);

View File

@ -12,7 +12,7 @@ export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<st
} }
if (followee.host != null) { if (followee.host != null) {
return `skip: フォローしようとしているユーザーはローカルユーザーではありません`; return `skip: user you are trying to follow is not a local user`;
} }
await follow(actor, followee, activity.id); await follow(actor, followee, activity.id);

View File

@ -6,7 +6,7 @@ import { relayRejected } from '@/services/relay.js';
import { Users } from '@/models/index.js'; import { Users } from '@/models/index.js';
export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => { export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<string> => {
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある // ※ `activity.actor` must be an existing local user, since `activity` is a follow request thrown from us.
const dbResolver = new DbResolver(); const dbResolver = new DbResolver();
const follower = await dbResolver.getUserFromApId(activity.actor); const follower = await dbResolver.getUserFromApId(activity.actor);

View File

@ -23,5 +23,5 @@ export default async (actor: CacheableRemoteUser, activity: IAccept): Promise<st
return `ok: unfollowed`; return `ok: unfollowed`;
} }
return `skip: フォローされていない`; return `skip: skip: not followed`;
}; };

View File

@ -13,7 +13,7 @@ export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<str
} }
if (blockee.host != null) { if (blockee.host != null) {
return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`; return `skip: The user you are trying to unblock is not a local user`;
} }
await unblock(await Users.findOneByOrFail({ id: actor.id }), blockee); await unblock(await Users.findOneByOrFail({ id: actor.id }), blockee);

View File

@ -14,7 +14,7 @@ export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<st
} }
if (followee.host != null) { if (followee.host != null) {
return `skip: フォロー解除しようとしているユーザーはローカルユーザーではありません`; return `skip: The user you are trying to unfollow is not a local user`;
} }
const req = await FollowRequests.findOneBy({ const req = await FollowRequests.findOneBy({
@ -37,5 +37,5 @@ export default async (actor: CacheableRemoteUser, activity: IFollow): Promise<st
return `ok: unfollowed`; return `ok: unfollowed`;
} }
return `skip: リクエストもフォローもされていない`; return `skip: Not requested or followed`;
}; };

View File

@ -6,7 +6,7 @@ import Resolver from '../../resolver.js';
import { updatePerson } from '../../models/person.js'; import { updatePerson } from '../../models/person.js';
/** /**
* Updateアクティビティを捌きます * Handler for the Update activity
*/ */
export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise<string> => { export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise<string> => {
if ('actor' in activity && actor.uri !== activity.actor) { if ('actor' in activity && actor.uri !== activity.actor) {

View File

@ -11,10 +11,10 @@ import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
const logger = apLogger; const logger = apLogger;
/** /**
* Imageを作成します * create an Image.
*/ */
export async function createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { export async function createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
// 投稿者が凍結されていたらスキップ // Skip if author is frozen.
if (actor.isSuspended) { if (actor.isSuspended) {
throw new Error('actor has been suspended'); throw new Error('actor has been suspended');
} }
@ -39,8 +39,8 @@ export async function createImage(actor: CacheableRemoteUser, value: any): Promi
}); });
if (file.isLink) { if (file.isLink) {
// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、 // If the URL is different, it means that the same image was previously
// URLを更新する // registered with a different URL, so update the URL
if (file.url !== image.url) { if (file.url !== image.url) {
await DriveFiles.update({ id: file.id }, { await DriveFiles.update({ id: file.id }, {
url: image.url, url: image.url,
@ -55,14 +55,14 @@ export async function createImage(actor: CacheableRemoteUser, value: any): Promi
} }
/** /**
* Imageを解決します * Resolve Image.
* *
* Misskeyに対象のImageが登録されていればそれを返し * If the target Image is registered in Calckey, return it, otherwise
* Misskeyに登録しそれを返します * Fetch from remote server, register with Calckey and return it.
*/ */
export async function resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { export async function resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> {
// TODO // TODO
// リモートサーバーからフェッチしてきて登録 // Fetch from remote server and register
return await createImage(actor, value); return await createImage(actor, value);
} }

View File

@ -53,9 +53,9 @@ export function validateNote(object: any, uri: string) {
} }
/** /**
* Noteをフェッチします * Fetch Notes.
* *
* Misskeyに対象のNoteが登録されていればそれを返します * If the target Note is registered in Calckey, it will be returned.
*/ */
export async function fetchNote(object: string | IObject): Promise<Note | null> { export async function fetchNote(object: string | IObject): Promise<Note | null> {
const dbResolver = new DbResolver(); const dbResolver = new DbResolver();
@ -63,7 +63,7 @@ export async function fetchNote(object: string | IObject): Promise<Note | null>
} }
/** /**
* Noteを作成します * Create a Note.
*/ */
export async function createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { export async function createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
if (resolver == null) resolver = new Resolver(); if (resolver == null) resolver = new Resolver();
@ -89,10 +89,10 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
logger.info(`Creating the Note: ${note.id}`); logger.info(`Creating the Note: ${note.id}`);
// 投稿者をフェッチ // Fetch author
const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser;
// 投稿者が凍結されていたらスキップ // Skip if author is suspended.
if (actor.isSuspended) { if (actor.isSuspended) {
throw new Error('actor has been suspended'); throw new Error('actor has been suspended');
} }
@ -101,10 +101,10 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
let visibility = noteAudience.visibility; let visibility = noteAudience.visibility;
const visibleUsers = noteAudience.visibleUsers; const visibleUsers = noteAudience.visibleUsers;
// Audience (to, cc) が指定されてなかった場合 // If Audience (to, cc) was not specified
if (visibility === 'specified' && visibleUsers.length === 0) { if (visibility === 'specified' && visibleUsers.length === 0) {
if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している if (typeof value === 'string') { // If the input is a string, GET occurs in resolver
// こちらから匿名GET出来たものならばpublic // Public if you can GET anonymously from here
visibility = 'public'; visibility = 'public';
} }
} }
@ -114,7 +114,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
const apMentions = await extractApMentions(note.tag); const apMentions = await extractApMentions(note.tag);
const apHashtags = await extractApHashtags(note.tag); const apHashtags = await extractApHashtags(note.tag);
// 添付ファイル // Attachments
// TODO: attachmentは必ずしもImageではない // TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない // TODO: attachmentは必ずしも配列ではない
// Noteがsensitiveなら添付もsensitiveにする // Noteがsensitiveなら添付もsensitiveにする
@ -127,7 +127,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
.filter(image => image != null) .filter(image => image != null)
: []; : [];
// リプライ // Reply
const reply: Note | null = note.inReplyTo const reply: Note | null = note.inReplyTo
? await resolveNote(note.inReplyTo, resolver).then(x => { ? await resolveNote(note.inReplyTo, resolver).then(x => {
if (x == null) { if (x == null) {
@ -153,7 +153,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
}) })
: null; : null;
// 引用 // Quote
let quote: Note | undefined | null; let quote: Note | undefined | null;
if (note._misskey_quote || note.quoteUrl) { if (note._misskey_quote || note.quoteUrl) {
@ -196,7 +196,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
const cw = note.summary === '' ? null : note.summary; const cw = note.summary === '' ? null : note.summary;
// テキストのパース // Text parsing
let text: string | null = null; let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') { if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') {
text = note.source.content; text = note.source.content;
@ -265,23 +265,23 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
} }
/** /**
* Noteを解決します * Resolve Note.
* *
* Misskeyに対象のNoteが登録されていればそれを返し * If the target Note is registered in Calckey, return it, otherwise
* Misskeyに登録しそれを返します * Fetch from remote server, register with Calckey and return it.
*/ */
export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> {
const uri = typeof value === 'string' ? value : value.id; const uri = typeof value === 'string' ? value : value.id;
if (uri == null) throw new Error('missing uri'); if (uri == null) throw new Error('missing uri');
// ブロックしてたら中断 // Abort if origin host is blocked
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`); if (meta.blockedHosts.includes(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
const unlock = await getApLock(uri); const unlock = await getApLock(uri);
try { try {
//#region このサーバーに既に登録されていたらそれを返す //#region Returns if already registered with this server
const exist = await fetchNote(uri); const exist = await fetchNote(uri);
if (exist) { if (exist) {
@ -293,9 +293,9 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note'); throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note');
} }
// リモートサーバーからフェッチしてきて登録 // Fetch from remote server and register
// ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにートが生成されるが // If the attached `Note` Object is specified here instead of the uri, the note will be generated without going through the server fetch.
// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 // Since the attached Note Object may be disguised, always specify the uri and fetch it from the server.
return await createNote(uri, resolver, true); return await createNote(uri, resolver, true);
} finally { } finally {
unlock(); unlock();

View File

@ -101,9 +101,9 @@ function validateActor(x: IObject, uri: string): IActor {
} }
/** /**
* Personをフェッチします * Fetch a Person.
* *
* Misskeyに対象のPersonが登録されていればそれを返します * If the target Person is registered in Calckey, it will be returned.
*/ */
export async function fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> { export async function fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> {
if (typeof uri !== 'string') throw new Error('uri is not string'); if (typeof uri !== 'string') throw new Error('uri is not string');
@ -111,7 +111,7 @@ export async function fetchPerson(uri: string, resolver?: Resolver): Promise<Cac
const cached = uriPersonCache.get(uri); const cached = uriPersonCache.get(uri);
if (cached) return cached; if (cached) return cached;
// URIがこのサーバーを指しているならデータベースからフェッチ // Fetch from the database if the URI points to this server
if (uri.startsWith(config.url + '/')) { if (uri.startsWith(config.url + '/')) {
const id = uri.split('/').pop(); const id = uri.split('/').pop();
const u = await Users.findOneBy({ id }); const u = await Users.findOneBy({ id });
@ -119,7 +119,7 @@ export async function fetchPerson(uri: string, resolver?: Resolver): Promise<Cac
return u; return u;
} }
//#region このサーバーに既に登録されていたらそれを返す //#region Returns if already registered with this server
const exist = await Users.findOneBy({ uri }); const exist = await Users.findOneBy({ uri });
if (exist) { if (exist) {
@ -132,7 +132,7 @@ export async function fetchPerson(uri: string, resolver?: Resolver): Promise<Cac
} }
/** /**
* Personを作成します * Create Person.
*/ */
export async function createPerson(uri: string, resolver?: Resolver): Promise<User> { export async function createPerson(uri: string, resolver?: Resolver): Promise<User> {
if (typeof uri !== 'string') throw new Error('uri is not string'); if (typeof uri !== 'string') throw new Error('uri is not string');
@ -210,7 +210,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
} catch (e) { } catch (e) {
// duplicate key error // duplicate key error
if (isDuplicateKeyValueError(e)) { if (isDuplicateKeyValueError(e)) {
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応 // /users/@a => /users/:id Corresponds to an error that may occur when the input is an alias like
const u = await Users.findOneBy({ const u = await Users.findOneBy({
uri: person.id, uri: person.id,
}); });
@ -235,10 +235,10 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
usersChart.update(user!, true); usersChart.update(user!, true);
// ハッシュタグ更新 // Hashtag update
updateUsertags(user!, tags); updateUsertags(user!, tags);
//#region アバターとヘッダー画像をフェッチ //#region Fetch avatar and header image
const [avatar, banner] = await Promise.all([ const [avatar, banner] = await Promise.all([
person.icon, person.icon,
person.image, person.image,
@ -260,7 +260,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
user!.bannerId = bannerId; user!.bannerId = bannerId;
//#endregion //#endregion
//#region カスタム絵文字取得 //#region Get custom emoji
const emojis = await extractEmojis(person.tag || [], host).catch(e => { const emojis = await extractEmojis(person.tag || [], host).catch(e => {
logger.info(`extractEmojis: ${e}`); logger.info(`extractEmojis: ${e}`);
return [] as Emoji[]; return [] as Emoji[];

View File

@ -43,10 +43,10 @@ export async function extractPollFromQuestion(source: string | IObject, resolver
export async function updateQuestion(value: any, resolver?: Resolver) { export async function updateQuestion(value: any, resolver?: Resolver) {
const uri = typeof value === 'string' ? value : value.id; const uri = typeof value === 'string' ? value : value.id;
// URIがこのサーバーを指しているならスキップ // Skip if URI points to this server
if (uri.startsWith(config.url + '/')) throw new Error('uri points local'); if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
//#region このサーバーに既に登録されているか //#region Already registered with this server?
const note = await Notes.findOneBy({ uri }); const note = await Notes.findOneBy({ uri });
if (note == null) throw new Error('Question is not registed'); if (note == null) throw new Error('Question is not registed');

View File

@ -6,7 +6,7 @@ import { updatePerson } from './models/person.js';
export default async (actor: CacheableRemoteUser, activity: IObject): Promise<void> => { export default async (actor: CacheableRemoteUser, activity: IObject): Promise<void> => {
await performActivity(actor, activity); await performActivity(actor, activity);
// ついでにリモートユーザーの情報が古かったら更新しておく // Update the remote user information if it is out of date
if (actor.uri) { if (actor.uri) {
if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
setImmediate(() => { setImmediate(() => {

View File

@ -58,7 +58,7 @@ export async function insertFollowingDoc(followee: { id: User['id']; host: User[
followerId: follower.id, followerId: follower.id,
}); });
// 通知を作成 // Create notification that request was accepted.
createNotification(follower.id, 'followRequestAccepted', { createNotification(follower.id, 'followRequestAccepted', {
notifierId: followee.id, notifierId: followee.id,
}); });

View File

@ -3,8 +3,8 @@ import { User } from '@/models/entities/user.js';
import { FollowRequests, Users } from '@/models/index.js'; import { FollowRequests, Users } from '@/models/index.js';
/** /**
* * Approve all follow requests for the specified user
* @param user * @param user User.
*/ */
export default async function(user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }) { export default async function(user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }) {
const requests = await FollowRequests.findBy({ const requests = await FollowRequests.findBy({

View File

@ -11,7 +11,7 @@ export async function doPostSuspend(user: { id: User['id']; host: User['host'] }
publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
if (Users.isLocalUser(user)) { if (Users.isLocalUser(user)) {
// 知り得る全SharedInboxにDelete配信 // Send Delete to all known SharedInboxes
const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user)); const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user));
const queue: string[] = []; const queue: string[] = [];