diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 334a798483..84062e5e25 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -255,20 +255,27 @@ export class ChatService { await redisPipeline.exec(); } + @bindThis + public findMyMessageById(userId: MiUser['id'], messageId: MiChatMessage['id']) { + return this.chatMessagesRepository.findOneBy({ id: messageId, fromUserId: userId }); + } + @bindThis public async deleteMessage(message: MiChatMessage) { await this.chatMessagesRepository.delete(message.id); if (message.toUserId) { - const fromUser = await this.usersRepository.findOneByOrFail({ id: message.fromUserId }); - const toUser = await this.usersRepository.findOneByOrFail({ id: message.toUserId }); + const [fromUser, toUser] = await Promise.all([ + this.usersRepository.findOneByOrFail({ id: message.fromUserId }), + this.usersRepository.findOneByOrFail({ id: message.toUserId }), + ]); if (this.userEntityService.isLocalUser(fromUser)) this.globalEventService.publishChatStream(message.fromUserId, message.toUserId, 'deleted', message.id); if (this.userEntityService.isLocalUser(toUser)) this.globalEventService.publishChatStream(message.toUserId, message.fromUserId, 'deleted', message.id); if (this.userEntityService.isLocalUser(fromUser) && this.userEntityService.isRemoteUser(toUser)) { - const activity = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), fromUser)); - this.queueService.deliver(fromUser, activity, toUser.inbox); + //const activity = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), fromUser)); + //this.queueService.deliver(fromUser, activity, toUser.inbox); } }/* else if (message.toRoomId) { this.globalEventService.publishRoomChatStream(message.toRoomId, 'deleted', message.id); diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts index 1428fca6cd..ba999e14fb 100644 --- a/packages/backend/src/server/api/endpoint-list.ts +++ b/packages/backend/src/server/api/endpoint-list.ts @@ -397,6 +397,7 @@ export * as 'users/search-by-username-and-host' from './endpoints/users/search-b 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/delete' from './endpoints/chat/messages/delete.js'; export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js'; export * as 'chat/history' from './endpoints/chat/history.js'; export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js'; diff --git a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts new file mode 100644 index 0000000000..ff4f4bd43f --- /dev/null +++ b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts @@ -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 { GetterService } from '@/server/api/GetterService.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: 'write:chat', + + res: { + }, + + errors: { + noSuchMessage: { + message: 'No such message.', + code: 'NO_SUCH_MESSAGE', + id: '36b67f0e-66a6-414b-83df-992a55294f17', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + messageId: { type: 'string', format: 'misskey:id' }, + }, + required: ['messageId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private chatService: ChatService, + ) { + super(meta, paramDef, async (ps, me) => { + const message = await this.chatService.findMyMessageById(me.id, ps.messageId); + if (message == null) { + throw new ApiError(meta.errors.noSuchMessage); + } + await this.chatService.deleteMessage(message); + }); + } +} diff --git a/packages/frontend/src/pages/chat/room.message.vue b/packages/frontend/src/pages/chat/room.message.vue index 3e60242be4..b2626b964d 100644 --- a/packages/frontend/src/pages/chat/room.message.vue +++ b/packages/frontend/src/pages/chat/room.message.vue @@ -26,15 +26,18 @@ diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 754b238272..110525d3f7 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -965,6 +965,12 @@ type ChatMessagesCreateRequest = operations['chat___messages___create']['request // @public (undocumented) type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json']; +// @public (undocumented) +type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; + // @public (undocumented) type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json']; @@ -1475,6 +1481,8 @@ declare namespace entities { ChatHistoryResponse, ChatMessagesCreateRequest, ChatMessagesCreateResponse, + ChatMessagesDeleteRequest, + ChatMessagesDeleteResponse, ChatMessagesTimelineRequest, ChatMessagesTimelineResponse, ClipsAddNoteRequest, diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index b3e330807f..8983f616e6 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1556,6 +1556,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 8514389bdd..61fd110bbf 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -211,6 +211,8 @@ import type { ChatHistoryResponse, ChatMessagesCreateRequest, ChatMessagesCreateResponse, + ChatMessagesDeleteRequest, + ChatMessagesDeleteResponse, ChatMessagesTimelineRequest, ChatMessagesTimelineResponse, ClipsAddNoteRequest, @@ -734,6 +736,7 @@ export type Endpoints = { 'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse }; 'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse }; 'chat/messages/create': { req: ChatMessagesCreateRequest; res: ChatMessagesCreateResponse }; + 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse }; 'chat/messages/timeline': { req: ChatMessagesTimelineRequest; res: ChatMessagesTimelineResponse }; 'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse }; 'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index 9f38b0689b..4a05c4f1bb 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -214,6 +214,8 @@ export type ChatHistoryRequest = operations['chat___history']['requestBody']['co export type ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json']; export type ChatMessagesCreateRequest = operations['chat___messages___create']['requestBody']['content']['application/json']; export type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json']; +export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; +export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; export type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json']; export type ChatMessagesTimelineResponse = operations['chat___messages___timeline']['responses']['200']['content']['application/json']; export type ClipsAddNoteRequest = operations['clips___add-note']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 6e0346b935..baf6d69ba2 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -1376,6 +1376,15 @@ export type paths = { */ post: operations['chat___messages___create']; }; + '/chat/messages/delete': { + /** + * chat/messages/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___messages___delete']; + }; '/chat/messages/timeline': { /** * chat/messages/timeline @@ -13844,6 +13853,60 @@ export type operations = { }; }; }; + /** + * chat/messages/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___messages___delete: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + messageId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @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']; + }; + }; + }; + }; /** * chat/messages/timeline * @description No description provided.