Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(matrix): add some api #138

Merged
merged 3 commits into from
Aug 5, 2023
Merged
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
44 changes: 38 additions & 6 deletions adapters/matrix/src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,19 @@ export class MatrixBot extends Bot<MatrixBot.Config> {
const sync = await this.syncRooms()
const joined = sync?.rooms?.join
if (!joined) return []
const result: string[] = []
for (const [roomId, room] of Object.entries(joined)) {
const create = room.state?.events?.find(event => event.type === 'm.room.create')
const space = (create?.content as Matrix.M_ROOM_CREATE)?.type === 'm.space'
if (space) result.push(roomId)
const result: Universal.Guild[] = []
for (const roomId of Object.keys(joined)) {
const state = await this.internal.getState(roomId)
const create = state.find(state => state.type === 'm.room.create')
const name = state.find(state => state.type === 'm.room.name')?.content as Matrix.M_ROOM_NAME
if (!create) continue
if (create.content['type'] !== 'm.space') continue
result.push({
guildId: roomId,
guildName: name?.name,
})
}
return await Promise.all(result.map(this.getGuild.bind(this)))
return result
}

async getChannelList(guildId: string): Promise<Universal.Channel[]> {
Expand All @@ -120,6 +126,32 @@ export class MatrixBot extends Bot<MatrixBot.Config> {
return await Promise.all(children.map(this.getChannel.bind(this)))
}

async getGuildMemberList(guildId: string): Promise<Universal.GuildMember[]> {
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<Universal.GuildMember> {
return (await this.getGuildMemberList(guildId)).find(user => user.userId === userId)
}

async createReaction(channelId: string, messageId: string, emoji: string): Promise<void> {
await this.internal.sendReaction(channelId, messageId, emoji)
}

async handleFriendRequest(): Promise<void> { }

// as utils.ts commented, messageId is roomId
Expand Down
4 changes: 2 additions & 2 deletions adapters/matrix/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class MatrixMessageEncoder extends MessageEncoder<MatrixBot> {
const session = this.bot.session(this.session)
const { data, filename, mime } = await this.bot.ctx.http.file(url)
const id = await this.bot.internal.sendMediaMessage(
this.channelId, this.bot.userId, type, Buffer.from(data), this.reply?.messageId, mime, filename,
this.channelId, type, Buffer.from(data), this.reply?.messageId, mime, filename,
)
session.messageId = id
this.results.push(session)
Expand All @@ -28,7 +28,7 @@ export class MatrixMessageEncoder extends MessageEncoder<MatrixBot> {
this.buffer = `> <${this.reply.userId}> ${this.reply.content}\n\n` + this.buffer
}
const id = await this.bot.internal.sendTextMessage(
this.channelId, this.bot.userId, this.buffer, this.reply?.messageId,
this.channelId, this.buffer, this.reply?.messageId,
)
session.messageId = id
this.results.push(session)
Expand Down
89 changes: 76 additions & 13 deletions adapters/matrix/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<M_ROOM_CREATE>
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 {
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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)

Expand All @@ -456,19 +489,19 @@ export class Internal {
return (await this.bot.http.post(`/media/v3/upload?filename=${filename}`, buffer, { headers })).content_uri
}

async sendTextMessage(roomId: string, userId: string, content: string, reply?: string): Promise<string> {
async sendTextMessage(roomId: string, content: string, reply?: string): Promise<string> {
const eventContent: M_TEXT = {
msgtype: 'm.text',
body: content,
}
if (reply) eventContent['m.relates_to'] = { 'm.in_reply_to': { 'event_id': reply } }
const response = await this.bot.http.put(
`/client/v3/rooms/${roomId}/send/m.room.message/${this.txnId++}?user_id=${userId}`, eventContent)
`/client/v3/rooms/${roomId}/send/m.room.message/${this.txnId++}`, eventContent)
return response.event_id
}

async sendMediaMessage(
roomId: string, userId: string, type: 'file' | 'image' | 'video' | 'audio',
roomId: string, type: 'file' | 'image' | 'video' | 'audio',
buffer: Buffer, reply?: string, mimetype?: string, filename: string = 'file',
): Promise<string> {
const uri = await this.uploadFile(filename, buffer, mimetype)
Expand All @@ -490,7 +523,20 @@ export class Internal {
}
if (reply) eventContent['m.relates_to'] = { 'm.in_reply_to': { 'event_id': reply } }
const response = await this.bot.http.put(
`/client/v3/rooms/${roomId}/send/m.room.message/${this.txnId++}?user_id=${userId}`, eventContent)
`/client/v3/rooms/${roomId}/send/m.room.message/${this.txnId++}`, eventContent)
return response.event_id
}

async sendReaction(roomId: string, messageId: string, key: string): Promise<string> {
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++}`, eventContent)
return response.event_id
}

Expand All @@ -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<RoomId> {
return await this.bot.http.post(`/client/v3/join/${roomId}`, { reason })
async createRoom(creation: RoomCreation): Promise<string> {
const response = await this.bot.http.post('/client/v3/createRoom', creation)
return response.room_id
}

async joinRoom(roomId: string, reason?: string): Promise<string> {
const response = await this.bot.http.post(`/client/v3/join/${roomId}`, { reason })
return response.room_id
}

async leaveRoom(roomId: string, reason?: string): Promise<string> {
const response = await this.bot.http.post(`/client/v3/rooms/${roomId}/leave`, { reason })
return response.room_id
}

async leaveRoom(roomId: string, reason?: string): Promise<RoomId> {
return await this.bot.http.post(`/client/v3/rooms/${roomId}/leave`, { reason })
async invite(roomId: string, userId: string, reason?: string): Promise<void> {
await this.bot.http.post(`/client/v3/rooms/${roomId}/invite`, { user_id: userId, reason })
}

async sync(fullSstate: boolean = false): Promise<Sync> {
Expand All @@ -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<string> {
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<string[]> {
return await this.bot.http.get('/client/v3/joined_rooms')
}
Expand Down
14 changes: 12 additions & 2 deletions adapters/matrix/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading