From 52956e52dd4cc5c86bb450012d7a83f511443bfd Mon Sep 17 00:00:00 2001 From: Anillc Date: Tue, 25 Jul 2023 15:19:26 +0800 Subject: [PATCH] feat(matrix): add some api --- adapters/matrix/src/bot.ts | 26 ++++++++++++ adapters/matrix/src/types.ts | 81 ++++++++++++++++++++++++++++++++---- adapters/matrix/src/utils.ts | 14 ++++++- 3 files changed, 110 insertions(+), 11 deletions(-) diff --git a/adapters/matrix/src/bot.ts b/adapters/matrix/src/bot.ts index 91f0fd2b..a3ac453b 100644 --- a/adapters/matrix/src/bot.ts +++ b/adapters/matrix/src/bot.ts @@ -120,6 +120,32 @@ export class MatrixBot extends Bot { return await Promise.all(children.map(this.getChannel.bind(this))) } + async getGuildMemberList(guildId: string): Promise { + const state = await this.internal.getState(guildId) + const levels = state.find(event => event.type === 'm.room.power_levels').content as Matrix.M_ROOM_POWER_LEVELS + return state + .filter(event => event.type === 'm.room.member') + .map(event => { + const content = event.content as Matrix.M_ROOM_MEMBER + return { + userId: event.state_key, + username: event.state_key, + nickname: content.displayname, + avatar: this.internal.getAssetUrl(content.avatar_url), + isBot: !!this.ctx.bots.find(bot => bot.userId === event.state_key), + roles: [levels.users[event.state_key].toString()] + } + }) + } + + async getGuildMember(guildId: string, userId: string): Promise { + return (await this.getGuildMemberList(guildId)).find(user => user.userId === userId) + } + + async createReaction(channelId: string, messageId: string, emoji: string): Promise { + await this.internal.sendReaction(channelId, this.userId, messageId, emoji) + } + async handleFriendRequest(): Promise { } // as utils.ts commented, messageId is roomId diff --git a/adapters/matrix/src/types.ts b/adapters/matrix/src/types.ts index b0c6707a..e8d7ebc1 100644 --- a/adapters/matrix/src/types.ts +++ b/adapters/matrix/src/types.ts @@ -120,10 +120,6 @@ export interface User { user_id?: string } -export interface RoomId { - room_id: string -} - export interface Sync { account_data?: AccountData // device_lists? // end to end @@ -211,6 +207,34 @@ export interface LeftRoom { timeline?: Timeline } +export interface Invite3pid { + address: string + id_access_token: string + id_server: string + medium: string +} + +export interface StateEvent { + content: EventContent + state_key?: string + type: string +} + +export interface RoomCreation { + creation_content?: Partial + initial_state?: StateEvent[] + invite?: string[] + invite_3pid?: Invite3pid + is_direct?: boolean + name: string + power_level_content_override?: M_ROOM_POWER_LEVELS + preset?: 'private_chat' | 'public_chat' | 'trusted_private_chat' + room_alias_name?: string + room_version?: string + topic?: string + visibility?: 'public' | 'private' +} + export interface EventContent {} export interface Relation { @@ -241,7 +265,7 @@ export interface M_ROOM_JOIN_RULES extends EventContent { export interface M_ROOM_MEMBER extends EventContent { avatar_url?: string - displayname?: string[] + displayname?: string is_direct?: boolean join_authorised_via_users_server?: string membership?: 'invite' | 'join' | 'knock' | 'leave' | 'ban' @@ -445,6 +469,15 @@ export interface M_SPACE_PARENT extends EventContent { via?: string[] } +export interface M_ANNOTATION extends Relation { + rel_type: 'm.annotation' + key: string +} + +export interface M_REACTION extends EventContent { + 'm.relates_to'?: M_ANNOTATION +} + export class Internal { private txnId = Math.round(Math.random() * 1000) @@ -494,6 +527,19 @@ export class Internal { return response.event_id } + async sendReaction(roomId: string, userId: string, messageId: string, key: string): Promise { + const eventContent: M_REACTION = { + 'm.relates_to': { + rel_type: 'm.annotation', + event_id: messageId, + key, + } + } + const response = await this.bot.http.put( + `/client/v3/rooms/${roomId}/send/m.reaction/${this.txnId++}?user_id=${userId}`, eventContent) + return response.event_id + } + async getEvent(roomId: string, eventId: string): Promise { return await this.bot.http.get(`/client/v3/rooms/${roomId}/event/${eventId}`) } @@ -516,12 +562,23 @@ export class Internal { await this.bot.http.put(`/client/v3/profile/${userId}/avatar_url`, { avatar_url: uri }) } - async joinRoom(roomId: string, reason?: string): Promise { - return await this.bot.http.post(`/client/v3/join/${roomId}`, { reason }) + async createRoom(creation: RoomCreation): Promise { + const response = await this.bot.http.post('/client/v3/createRoom', creation) + return response.room_id + } + + async joinRoom(roomId: string, reason?: string): Promise { + const response = await this.bot.http.post(`/client/v3/join/${roomId}`, { reason }) + return response.room_id + } + + async leaveRoom(roomId: string, reason?: string): Promise { + const response = await this.bot.http.post(`/client/v3/rooms/${roomId}/leave`, { reason }) + return response.room_id } - async leaveRoom(roomId: string, reason?: string): Promise { - return await this.bot.http.post(`/client/v3/rooms/${roomId}/leave`, { reason }) + async invite(roomId: string, userId: string, reason?: string): Promise { + await this.bot.http.post(`/client/v3/rooms/${roomId}/invite`, { user_id: userId, reason }) } async sync(fullSstate: boolean = false): Promise { @@ -534,6 +591,12 @@ export class Internal { return await this.bot.http.get(`/client/v3/rooms/${roomId}/state`) } + async setState(roomId: string, eventType: string, event: EventContent, state?: string): Promise { + const statePath = state ? `/${state}` : '' + const response = await this.bot.http.put(`/client/v3/rooms/${roomId}/state/${eventType}${statePath}`, event) + return response.event_id + } + async getJoinedRooms(): Promise { return await this.bot.http.get('/client/v3/joined_rooms') } diff --git a/adapters/matrix/src/utils.ts b/adapters/matrix/src/utils.ts index cf5bdaea..76024d0f 100644 --- a/adapters/matrix/src/utils.ts +++ b/adapters/matrix/src/utils.ts @@ -69,10 +69,20 @@ export async function adaptSession(bot: MatrixBot, event: Matrix.ClientEvent): P session.timestamp = event.origin_server_ts session.author = adaptAuthor(bot, event) switch (event.type) { - case 'm.room.redaction': - session.type = 'message-delete' + case 'm.room.redaction': { + session.type = 'message-deleted' + session.subtype = 'group' session.messageId = event.redacts break + } + case 'm.reaction': { + const content = event.content as Matrix.M_REACTION + session.type = 'reaction-added' + session.subtype = 'group' + session.content = content['m.relates_to'].key + session.messageId = content['m.relates_to'].event_id + break + } case 'm.room.member': { bot.syncRooms() const memberEvent = event.content as Matrix.M_ROOM_MEMBER