Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/backend/src/core/ChatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { emojiRegex } from '@/misc/emoji-regex.js';
import { NotificationService } from '@/core/NotificationService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
import { trackPromise } from '@/misc/promise-tracker.js';

const MAX_ROOM_MEMBERS = 50;
const MAX_REACTIONS_PER_MESSAGE = 100;
Expand Down Expand Up @@ -81,6 +83,7 @@ export class ChatService {
private chatEntityService: ChatEntityService,
private idService: IdService,
private globalEventService: GlobalEventService,
private apDeliverManagerService: ApDeliverManagerService,
private apRendererService: ApRendererService,
private queueService: QueueService,
private pushNotificationService: PushNotificationService,
Expand Down Expand Up @@ -236,6 +239,19 @@ export class ChatService {
}, 3000);
}

//#region AP deliver
if (this.userEntityService.isLocalUser(fromUser) && this.userEntityService.isRemoteUser(toUser)) {
(async () => {
const content = await this.apRendererService.renderChatMessage(inserted, false);
const activity = this.apRendererService.addContext(content);

const dm = this.apDeliverManagerService.createDeliverManager(fromUser, activity);
dm.addDirectRecipe(toUser);
trackPromise(dm.execute());
})();
}
//#endregion

return packedMessage;
}

Expand Down
30 changes: 30 additions & 0 deletions packages/backend/src/core/activitypub/ApInboxService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,36 @@ export class ApInboxService {
}
}

@bindThis
private async chatMessage(resolver: Resolver, actor: MiRemoteUser, message: IObject): Promise<string> {
const uri = getApId(message);

if (typeof message === 'object') {
if (actor.uri !== message.attributedTo) {
return 'skip: actor.uri !== message.attributedTo';
}

if (typeof message.id === 'string') {
if (this.utilityService.extractDbHost(actor.uri) !== this.utilityService.extractDbHost(message.id)) {
return 'skip: host in actor.uri !== message.id';
}
} else {
return 'skip: message.id is not a string';
}
}

try {
await this.chatService.createMessageViaAp(message, actor, resolver);
return 'ok';
} catch (err) {
if (err instanceof StatusError && !err.isRetryable) {
return `skip ${err.statusCode}`;
} else {
throw err;
}
}
}

@bindThis
private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
if (actor.uri !== activity.actor) {
Expand Down
27 changes: 26 additions & 1 deletion packages/backend/src/core/activitypub/ApRendererService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { MfmService, type Appender } from '@/core/MfmService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import type { MiUserKeypair } from '@/models/UserKeypair.js';
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository, MiMeta } from '@/models/_.js';
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository, MiMeta, MiChatMessage } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { IdService } from '@/core/IdService.js';
Expand Down Expand Up @@ -502,6 +502,31 @@ export class ApRendererService {
};
}

@bindThis
public async renderChatMessage(message: MiChatMessage, dive = true): Promise<IPost> {
const attributedTo = this.userEntityService.genLocalUserUri(message.fromUserId);

const file = message.fileId ? await this.driveFilesRepository.findOneBy({ id: message.fileId }) : null;

const emojis = await this.getEmojis(message.emojis);
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));

const tag = [
...apemojis,
];

return {
id: `${this.config.url}/chat-messages/${message.id}`,
type: 'Misskey:ChatMessage',
attributedTo,
text: message.text,
published: this.idService.parse(message.id).date.toISOString(),
to: message.toUserId,
attachment: file ? [this.renderDocument(file)] : [],
tag,
};
}

@bindThis
public async renderPerson(user: MiLocalUser) {
const id = this.userEntityService.genLocalUserUri(user.id);
Expand Down
20 changes: 20 additions & 0 deletions packages/backend/src/models/ChatMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export class MiChatMessage {
})
public text: string | null;

// 連合用
// ローカルはnull
@Column('varchar', {
length: 512, nullable: true,
})
Expand Down Expand Up @@ -82,4 +84,22 @@ export class MiChatMessage {
length: 1024, array: true, default: '{}',
})
public reactions: string[];

// 連合用
@Column('varchar', {
length: 128, array: true, default: '{}',
})
public emojis: string[];

// 連合用
@Column('boolean', {
default: false,
})
public isDelivering: boolean;

// 連合用
@Column('boolean', {
default: false,
})
public isDeliverFailed: boolean;
}
Loading