diff --git a/README.md b/README.md index a1897b924..cbfee2f8e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ ## 細かい変更点 +- Pleroma のチャットに対応(Catodon から取り込み) - 翻訳機能にて、投稿言語が指定されていない場合にのみ言語の自動検出を用いるように変更 - アップデート時に更新内容を確認できる機能を追加 - 依存ライブラリを最新版にアップデート diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index a5c16c889..1a36b8bfa 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -129,6 +129,12 @@ export async function createNote( throw new Error(`unexpected schema of note.id: ${note.id}`); } + // ChatMessage only have id + // TODO: split into a separate validate function + if (note.type === "ChatMessage") { + note.url = note.id; + } + const url = getOneApHrefNullable(note.url); if (url && !url.startsWith("https://")) { @@ -183,7 +189,9 @@ export async function createNote( } } - let isTalk = note._misskey_talk && visibility === "specified"; + let isTalk = + (note.type === "ChatMessage" || note._misskey_talk) && + visibility === "specified"; const apMentions = await extractApMentions(note.tag); const apHashtags = await extractApHashtags(note.tag); diff --git a/packages/backend/src/remote/activitypub/renderer/index.ts b/packages/backend/src/remote/activitypub/renderer/index.ts index c60a1f4cd..734d1198c 100644 --- a/packages/backend/src/remote/activitypub/renderer/index.ts +++ b/packages/backend/src/remote/activitypub/renderer/index.ts @@ -49,6 +49,10 @@ export const renderActivity = (x: any): IActivity | null => { fedibird: "http://fedibird.com/ns#", // vcard vcard: "http://www.w3.org/2006/vcard/ns#", + // ChatMessage + litepub: "http://litepub.social/ns#", + ChatMessage: "litepub:ChatMessage", + directMessage: "litepub:directMessage", }, ], }, diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/remote/activitypub/type.ts index cf0410767..92889380a 100644 --- a/packages/backend/src/remote/activitypub/type.ts +++ b/packages/backend/src/remote/activitypub/type.ts @@ -115,6 +115,7 @@ export const validPost = [ "Page", "Video", "Event", + "ChatMessage", // TODO: move it to vaildMessage ]; export const isPost = (object: IObject): object is IPost => @@ -130,6 +131,7 @@ export interface IPost extends IObject { | "Image" | "Page" | "Video" + | "ChatMessage" // TODO: move it to IChatMessage | "Event"; source?: { content: string; diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index 506f29996..0b3f8eded 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -22,6 +22,8 @@ import renderNote from "@/remote/activitypub/renderer/note.js"; import renderCreate from "@/remote/activitypub/renderer/create.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import { deliver } from "@/queue/index.js"; +import { toPuny } from "@/misc/convert-host.js"; +import { Instances } from "@/models/index.js"; export async function createMessage( user: { id: User["id"]; host: User["host"] }, @@ -121,6 +123,9 @@ export async function createMessage( Users.isLocalUser(user) && Users.isRemoteUser(recipientUser) ) { + const instance = await Instances.findOneBy({ + host: toPuny(recipientUser.host), + }); const note = { id: message.id, createdAt: message.createdAt, @@ -138,10 +143,43 @@ export async function createMessage( ), } as Note; - const activity = renderActivity( - renderCreate(await renderNote(note, false, true), note), + let renderedNote: Record = await renderNote( + note, + false, + true, ); + // TODO: For pleroma and its fork instances, the actor will have a boolean "capabilities": { acceptsChatMessages: boolean } property. May use that instead of checking instance.softwareName. https://kazv.moe/objects/ca5c0b88-88ce-48a7-bf88-54d45f6ce781 + // ChatMessage document from Pleroma: https://docs.pleroma.social/backend/development/ap_extensions/#chatmessages + // Note: LitePub has been stalled since 2019-06-29 and is incomplete as a specification + if ( + instance?.softwareName && + ["akkoma", "pleroma", "lemmy"].includes( + instance.softwareName.toLowerCase(), + ) + ) { + const tmp_note = renderedNote; + renderedNote = { + type: "ChatMessage", + attributedTo: tmp_note.attributedTo, + content: tmp_note.content, + id: tmp_note.id, + published: tmp_note.published, + to: tmp_note.to, + tag: tmp_note.tag, + cc: [], + }; + // A recently fixed bug, empty arrays will be rejected by pleroma + if ( + Array.isArray(tmp_note.attachment) && + tmp_note.attachment.length !== 0 + ) { + renderedNote.attachment = tmp_note.attachment; + } + } + + const activity = renderActivity(renderCreate(renderedNote, note)); + deliver(user, activity, recipientUser.inbox); } return messageObj;