enhance(backend): push notification for chat message

Resolve #15831
This commit is contained in:
syuilo 2025-04-15 18:27:45 +09:00
parent fff2783c1b
commit fc6037af46
7 changed files with 36 additions and 4 deletions

View File

@ -1,7 +1,7 @@
## Unreleased ## Unreleased
### General ### General
- - Enhance: チャットの新規メッセージをプッシュ通知するように
### Client ### Client
- Feat: チャットウィジェットを追加 - Feat: チャットウィジェットを追加

View File

@ -232,7 +232,7 @@ export class ChatService {
const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted, toUser); const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted, toUser);
this.globalEventService.publishMainStream(toUser.id, 'newChatMessage', packedMessageForTo); this.globalEventService.publishMainStream(toUser.id, 'newChatMessage', packedMessageForTo);
//this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo); this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo);
}, 3000); }, 3000);
} }
@ -302,7 +302,7 @@ export class ChatService {
if (marker == null) continue; if (marker == null) continue;
this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo);
//this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo);
} }
}, 3000); }, 3000);

View File

@ -22,6 +22,7 @@ type PushNotificationsTypes = {
note: Packed<'Note'>; note: Packed<'Note'>;
}; };
'readAllNotifications': undefined; 'readAllNotifications': undefined;
newChatMessage: Packed<'ChatMessage'>;
}; };
// Reduce length because push message servers have character limits // Reduce length because push message servers have character limits

View File

@ -268,6 +268,24 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif
data, data,
renotify: true, renotify: true,
}]; }];
case 'newChatMessage':
if (data.body.toRoom != null) {
return [`${data.body.toRoom.name}: ${getUserName(data.body.fromUser)}: ${data.body.text}`, {
icon: data.body.fromUser.avatarUrl,
badge: iconUrl('messages'),
tag: `chat:room:${data.body.toRoomId}`,
data,
renotify: true,
}];
} else {
return [`${getUserName(data.body.fromUser)}: ${data.body.text}`, {
icon: data.body.fromUser.avatarUrl,
badge: iconUrl('messages'),
tag: `chat:user:${data.body.fromUserId}`,
data,
renotify: true,
}];
}
default: default:
return null; return null;
} }

View File

@ -16,7 +16,7 @@ export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise
export async function api< export async function api<
E extends keyof Misskey.Endpoints, E extends keyof Misskey.Endpoints,
P extends Misskey.Endpoints[E]['req'] P extends Misskey.Endpoints[E]['req'],
>(endpoint: E, userId?: string, params?: P): Promise<Misskey.api.SwitchCaseResponseType<E, P> | undefined> { >(endpoint: E, userId?: string, params?: P): Promise<Misskey.api.SwitchCaseResponseType<E, P> | undefined> {
let account: Pick<Misskey.entities.SignupResponse, 'id' | 'token'> | undefined; let account: Pick<Misskey.entities.SignupResponse, 'id' | 'token'> | undefined;
@ -60,6 +60,14 @@ export function openAntenna(antennaId: string, loginId: string): ReturnType<type
return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId }); return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId });
} }
export function openChat(body: any, loginId: string): ReturnType<typeof openClient> {
if (body.toRoomId != null) {
return openClient('push', `/chat/room/${body.toRoomId}`, loginId, { body });
} else {
return openClient('push', `/chat/user/${body.toUserId}`, loginId, { body });
}
}
// post-formのオプションから投稿フォームを開く // post-formのオプションから投稿フォームを開く
export async function openPost(options: { initialText?: string; reply?: Misskey.entities.Note; renote?: Misskey.entities.Note }, loginId?: string): ReturnType<typeof openClient> { export async function openPost(options: { initialText?: string; reply?: Misskey.entities.Note; renote?: Misskey.entities.Note }, loginId?: string): ReturnType<typeof openClient> {
// クエリを作成しておく // クエリを作成しておく

View File

@ -76,6 +76,7 @@ globalThis.addEventListener('push', ev => {
// case 'driveFileCreated': // case 'driveFileCreated':
case 'notification': case 'notification':
case 'unreadAntennaNote': case 'unreadAntennaNote':
case 'newChatMessage':
// 1日以上経過している場合は無視 // 1日以上経過している場合は無視
if (Date.now() - data.dateTime > 1000 * 60 * 60 * 24) break; if (Date.now() - data.dateTime > 1000 * 60 * 60 * 24) break;
@ -155,6 +156,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv
case 'unreadAntennaNote': case 'unreadAntennaNote':
client = await swos.openAntenna(data.body.antenna.id, loginId); client = await swos.openAntenna(data.body.antenna.id, loginId);
break; break;
case 'newChatMessage':
client = await swos.openChat(data.body, loginId);
break;
default: default:
switch (action) { switch (action) {
case 'markAllAsRead': case 'markAllAsRead':

View File

@ -23,6 +23,7 @@ type PushNotificationDataSourceMap = {
note: Misskey.entities.Note; note: Misskey.entities.Note;
}; };
readAllNotifications: undefined; readAllNotifications: undefined;
newChatMessage: Misskey.entities.ChatMessage;
}; };
export type PushNotificationData<K extends keyof PushNotificationDataSourceMap> = { export type PushNotificationData<K extends keyof PushNotificationDataSourceMap> = {