This commit is contained in:
syuilo
2025-03-17 18:00:31 +09:00
parent 9e5fb89408
commit 30be29a785
13 changed files with 544 additions and 8 deletions

View File

@@ -16,7 +16,7 @@ import { ChatMessageEntityService } from '@/core/entities/ChatMessageEntityServi
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
import { bindThis } from '@/decorators.js';
import type { ChatMessagesRepository, MiChatMessage, MiDriveFile, MiUser, UsersRepository } from '@/models/_.js';
import type { ChatMessagesRepository, MiChatMessage, MiDriveFile, MiUser, MutingsRepository, UsersRepository } from '@/models/_.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { QueryService } from '@/core/QueryService.js';
@@ -35,6 +35,9 @@ export class ChatService {
@Inject(DI.chatMessagesRepository)
private chatMessagesRepository: ChatMessagesRepository,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
private userEntityService: UserEntityService,
private chatMessageEntityService: ChatMessageEntityService,
private idService: IdService,
@@ -278,4 +281,82 @@ export class ChatService {
return messages;
}
@bindThis
public async userHistory(meId: MiUser['id'], limit: number) {
const history: MiChatMessage[] = [];
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
.select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: meId });
for (let i = 0; i < limit; i++) {
const found = history.map(m => (m.fromUserId === meId) ? m.toUserId! : m.fromUserId!);
const query = this.chatMessagesRepository.createQueryBuilder('message')
.orderBy('message.id', 'DESC')
.where(new Brackets(qb => {
qb
.where('message.fromUserId = :meId', { meId: meId })
.orWhere('message.toUserId = :meId', { meId: meId });
}))
.andWhere('message.groupId IS NULL')
.andWhere(`message.fromUserId NOT IN (${ mutingQuery.getQuery() })`)
.andWhere(`message.toUserId NOT IN (${ mutingQuery.getQuery() })`);
if (found.length > 0) {
query.andWhere('message.fromUserId NOT IN (:...found)', { found: found });
query.andWhere('message.toUserId NOT IN (:...found)', { found: found });
}
query.setParameters(mutingQuery.getParameters());
const message = await query.getOne();
if (message) {
history.push(message);
} else {
break;
}
}
return history;
}
@bindThis
public async groupHistory(meId: MiUser['id'], limit: number) {
/*
const groups = await this.userGroupJoiningsRepository.findBy({
userId: meId,
}).then(xs => xs.map(x => x.userGroupId));
if (groups.length === 0) {
return [];
}
const history: MiChatMessage[] = [];
for (let i = 0; i < limit; i++) {
const found = history.map(m => m.groupId!);
const query = this.chatMessagesRepository.createQueryBuilder('message')
.orderBy('message.id', 'DESC')
.where('message.groupId IN (:...groups)', { groups: groups });
if (found.length > 0) {
query.andWhere('message.groupId NOT IN (:...found)', { found: found });
}
const message = await query.getOne();
if (message) {
history.push(message);
} else {
break;
}
}
return history;
*/
}
}

View File

@@ -399,4 +399,5 @@ export * as 'users/show' from './endpoints/users/show.js';
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
export * as 'chat/messages/create' from './endpoints/chat/messages/create.js';
export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js';
export * as 'chat/messages/history' from './endpoints/chat/messages/history.js';
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';

View File

@@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { ChatService } from '@/core/ChatService.js';
import { ChatMessageEntityService } from '@/core/entities/ChatMessageEntityService.js';
import { ApiError } from '@/server/api/error.js';
export const meta = {
tags: ['chat'],
requireCredential: true,
kind: 'read:chat',
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'ChatMessage',
},
},
errors: {
},
} as const;
export const paramDef = {
type: 'object',
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
group: { type: 'boolean', default: false },
},
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private chatMessageEntityService: ChatMessageEntityService,
private chatService: ChatService,
) {
super(meta, paramDef, async (ps, me) => {
const history = ps.group ? await this.chatService.groupHistory(me.id, ps.limit) : await this.chatService.userHistory(me.id, ps.limit);
return await this.chatMessageEntityService.packMany(history, me);
});
}
}

View File

@@ -28,7 +28,7 @@ class ChatChannel extends Channel {
if (typeof params.otherId !== 'string') return;
this.otherId = params.otherId;
this.subscriber.on(`chatStream:${this.user.id}-${this.otherId}`, this.onEvent);
this.subscriber.on(`chatStream:${this.user!.id}-${this.otherId}`, this.onEvent);
}
@bindThis
@@ -39,7 +39,7 @@ class ChatChannel extends Channel {
@bindThis
public dispose() {
// Unsubscribe events
this.subscriber.off(`chatStream:${this.user.id}-${this.otherId}`, this.onEvent);
this.subscriber.off(`chatStream:${this.user!.id}-${this.otherId}`, this.onEvent);
}
}