diff --git a/adapters/discord/src/utils.ts b/adapters/discord/src/utils.ts index 81e4f1d6..b8e4189e 100644 --- a/adapters/discord/src/utils.ts +++ b/adapters/discord/src/utils.ts @@ -14,7 +14,7 @@ export const decodeUser = (user: Discord.User): Universal.User => ({ id: user.id, name: user.username, userId: user.id, - avatar: `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`, + avatar: user.avatar && `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`, username: user.username, discriminator: user.discriminator, isBot: user.bot || false, diff --git a/adapters/matrix/src/bot.ts b/adapters/matrix/src/bot.ts index e5401317..ed7008ed 100644 --- a/adapters/matrix/src/bot.ts +++ b/adapters/matrix/src/bot.ts @@ -2,7 +2,7 @@ import { Bot, Context, omit, Quester, Schema, Universal } from '@satorijs/satori import { HttpAdapter } from './http' import { MatrixMessageEncoder } from './message' import * as Matrix from './types' -import { adaptMessage, dispatchSession } from './utils' +import { adaptMessage, decodeUser, dispatchSession } from './utils' export class MatrixBot extends Bot { static MessageEncoder = MatrixMessageEncoder @@ -17,7 +17,8 @@ export class MatrixBot extends Bot { super(ctx, config) this.id = config.id this.platform = 'matrix' - this.userId = `@${this.id}:${this.config.host}` + this.user.id = `@${this.id}:${this.config.host}` + this.user.name = config.name || this.id this.endpoint = (config.endpoint || `https://${config.host}`) + '/_matrix' this.internal = new Matrix.Internal(this) ctx.plugin(HttpAdapter, this) @@ -73,21 +74,14 @@ export class MatrixBot extends Bot { async getUser(userId: string) { const profile = await this.internal.getProfile(userId) - let avatar: string - if (profile.avatar_url) avatar = this.internal.getAssetUrl(profile.avatar_url) - return { - id: userId, - name: profile.displayname, - userId, - avatar, - username: userId, - nickname: profile.displayname, - } + const user = decodeUser(profile, userId) + user.avatar = user.avatar && this.internal.getAssetUrl(user.avatar) + return user } async getGuild(guildId: string) { const { id, name } = await this.getChannel(guildId) - return { id, name, guildId, guildName: name } + return { id, name } } async getChannel(id: string) { @@ -114,14 +108,7 @@ export class MatrixBot extends Bot { .map(event => { const content = event.content as Matrix.M_ROOM_MEMBER return { - user: { - id: event.state_key, - name: event.state_key, - }, - userId: event.state_key, - username: event.state_key, - nickname: content.displayname, - avatar: this.internal.getAssetUrl(content.avatar_url), + user: decodeUser(content, event.state_key), isBot: !!this.ctx.bots.find(bot => bot.userId === event.state_key), roles: [levels.users[event.state_key].toString()], } @@ -130,7 +117,8 @@ export class MatrixBot extends Bot { } async getGuildMember(guildId: string, userId: string) { - return (await this.getGuildMemberList(guildId)).data.find(user => user.userId === userId) + const { data } = await this.getGuildMemberList(guildId) + return data.find(member => member.user.id === userId) } async createReaction(channelId: string, messageId: string, emoji: string) { diff --git a/adapters/matrix/src/message.ts b/adapters/matrix/src/message.ts index fa255d69..f4dd6fdd 100644 --- a/adapters/matrix/src/message.ts +++ b/adapters/matrix/src/message.ts @@ -10,7 +10,7 @@ export class MatrixMessageEncoder extends MessageEncoder { 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, type, Buffer.from(data), this.reply?.messageId, mime, filename, + this.channelId, type, Buffer.from(data), this.reply?.id, mime, filename, ) session.messageId = id this.results.push(session) @@ -25,10 +25,10 @@ export class MatrixMessageEncoder extends MessageEncoder { try { const session = this.bot.session(this.session) if (this.reply) { - this.buffer = `> <${this.reply.userId}> ${this.reply.content}\n\n` + this.buffer + this.buffer = `> <${this.reply.user.id}> ${this.reply.content}\n\n` + this.buffer } const id = await this.bot.internal.sendTextMessage( - this.channelId, this.buffer, this.reply?.messageId, + this.channelId, this.buffer, this.reply?.id, ) session.messageId = id this.results.push(session) diff --git a/adapters/matrix/src/utils.ts b/adapters/matrix/src/utils.ts index 8f1685b0..8d8506c2 100644 --- a/adapters/matrix/src/utils.ts +++ b/adapters/matrix/src/utils.ts @@ -3,32 +3,29 @@ import { MatrixBot } from './bot' import * as Matrix from './types' import { INode, ITag, parse, SyntaxKind } from 'html5parser' -export function adaptAuthor(bot: MatrixBot, event: Matrix.ClientEvent): Universal.Author { - return { - userId: event.sender, - username: event.sender, - isBot: !!bot.ctx.bots.find(bot => bot.userId === event.sender), - } -} +export const decodeUser = (data: Matrix.Profile, id: string): Universal.User => ({ + id, + name: data.displayname, + avatar: data.avatar_url, +}) -export async function adaptMessage(bot: MatrixBot, event: Matrix.ClientEvent, result: Universal.Message = {}): Promise { - result.messageId = event.event_id - result.channelId = event.room_id - result.guildId = event.room_id - result.userId = event.sender - result.timestamp = event.origin_server_ts - result.author = adaptAuthor(bot, event) - result.isDirect = false - const content = event.content as Matrix.M_ROOM_MESSAGE +export async function adaptMessage( + bot: MatrixBot, + data: Matrix.ClientEvent, + message: Universal.Message = {}, + payload: Universal.Message | Universal.EventData = message, +): Promise { + message.id = message.messageId = data.event_id + const content = data.content as Matrix.M_ROOM_MESSAGE const reply = content['m.relates_to']?.['m.in_reply_to'] if (reply) { - result.quote = await bot.getMessage(event.room_id, reply.event_id) + message.quote = await bot.getMessage(data.room_id, reply.event_id) } switch (content.msgtype) { case 'm.text': case 'm.emote': case 'm.notice': { - result.content = parsseContent(bot, content) + message.content = parsseContent(bot, content) break } case 'm.image': @@ -37,15 +34,29 @@ export async function adaptMessage(bot: MatrixBot, event: Matrix.ClientEvent, re case 'm.video': { const url = bot.internal.getAssetUrl((content as any).url) const type = content.msgtype.substring(2) - result.content = segment(type === 'audio' ? 'record' : type, { url }).toString() + message.content = segment(type === 'audio' ? 'record' : type, { url }).toString() break } default: return null } // result.content is not a setter if result is a Universal.Message - result.elements ??= segment.parse(result.content) - return result + message.elements ??= segment.parse(message.content) + + if (!payload) return message + payload.timestamp = data.origin_server_ts + payload.channel = { + id: data.room_id, + type: Universal.Channel.Type.TEXT, + } + payload.guild = { + id: data.room_id, + } + payload.user = { + id: data.sender, + } + payload.member = {} + return message } export async function adaptSession(bot: MatrixBot, event: Matrix.ClientEvent): Promise { @@ -60,7 +71,7 @@ export async function adaptSession(bot: MatrixBot, event: Matrix.ClientEvent): P } else { session.type = 'message' } - if (!await adaptMessage(bot, event, session)) return null + if (!await adaptMessage(bot, event, session.data.message = {}, session.data)) return null return session } session.userId = event.sender @@ -68,7 +79,6 @@ export async function adaptSession(bot: MatrixBot, event: Matrix.ClientEvent): P session.channelId = event.room_id session.messageId = event.event_id session.timestamp = event.origin_server_ts - session.author = adaptAuthor(bot, event) session.isDirect = false switch (event.type) { case 'm.room.redaction': { @@ -88,7 +98,7 @@ export async function adaptSession(bot: MatrixBot, event: Matrix.ClientEvent): P case 'm.room.member': { bot.syncRooms() const memberEvent = event.content as Matrix.M_ROOM_MEMBER - session.targetId = (memberEvent as any).state_key + session['targetId'] = (memberEvent as any).state_key session.operatorId = event.sender session.messageId = event.event_id if (memberEvent.reason) { diff --git a/adapters/onebot/src/bot/base.ts b/adapters/onebot/src/bot/base.ts index 117fbce6..fb9319b9 100644 --- a/adapters/onebot/src/bot/base.ts +++ b/adapters/onebot/src/bot/base.ts @@ -28,18 +28,18 @@ export class BaseBot extends Bot { async getLogin() { const data = await this.internal.getLoginInfo() - this.user = OneBot.adaptUser(data) + this.user = OneBot.decodeUser(data) return this.toJSON() } async getUser(userId: string) { const data = await this.internal.getStrangerInfo(userId) - return OneBot.adaptUser(data) + return OneBot.decodeUser(data) } async getFriendList() { const data = await this.internal.getFriendList() - return { data: data.map(OneBot.adaptUser) } + return { data: data.map(OneBot.decodeUser) } } async handleFriendRequest(messageId: string, approve: boolean, comment?: string) { diff --git a/adapters/onebot/src/bot/index.ts b/adapters/onebot/src/bot/index.ts index c3f97a7a..dac45600 100644 --- a/adapters/onebot/src/bot/index.ts +++ b/adapters/onebot/src/bot/index.ts @@ -76,12 +76,12 @@ export class OneBotBot extends Ba async getGuildMember(guildId: string, userId: string) { const data = await this.internal.getGroupMemberInfo(guildId, userId) - return OneBot.adaptGuildMember(data) + return OneBot.decodeGuildMember(data) } async getGuildMemberList(guildId: string) { const data = await this.internal.getGroupMemberList(guildId) - return { data: data.map(OneBot.adaptGuildMember) } + return { data: data.map(OneBot.decodeGuildMember) } } async kickGuildMember(guildId: string, userId: string, permanent?: boolean) { diff --git a/adapters/onebot/src/bot/message.ts b/adapters/onebot/src/bot/message.ts index 7ea5b7db..7b5aba79 100644 --- a/adapters/onebot/src/bot/message.ts +++ b/adapters/onebot/src/bot/message.ts @@ -1,7 +1,11 @@ -import { h, MessageEncoder, pick } from '@satorijs/satori' +import { h, MessageEncoder, pick, Universal } from '@satorijs/satori' import { BaseBot } from './base' import { CQCode } from './cqcode' -import { Author } from '../types' + +export interface Author extends Universal.User { + time?: string | number + messageId?: string +} class State { author: Partial = {} @@ -58,8 +62,8 @@ export class OneBotMessageEncoder extends MessageEncoder { this.stack[1].children.push({ type: 'node', data: { - name: author.nickname || author.username || this.bot.username, - uin: author.userId || this.bot.userId, + name: author.nick || author.name || this.bot.user.name, + uin: author.id || this.bot.userId, content: this.children as any, time: `${Math.floor((+author.time || Date.now()) / 1000)}`, }, diff --git a/adapters/onebot/src/index.ts b/adapters/onebot/src/index.ts index 30186e1d..393de685 100644 --- a/adapters/onebot/src/index.ts +++ b/adapters/onebot/src/index.ts @@ -9,17 +9,15 @@ export * from './ws' export default OneBotBot -declare global { - namespace Satori { - interface Session { - onebot?: OneBot.Payload & OneBot.Internal - } +declare module '@satorijs/core' { + interface Session { + onebot?: OneBot.Payload & OneBot.Internal + } - interface Events { - 'onebot/message-reactions-updated': {} - 'onebot/channel-updated': {} - 'onebot/channel-created': {} - 'onebot/channel-destroyed': {} - } + interface Events { + 'onebot/message-reactions-updated'(session: Session): void + 'onebot/channel-updated'(session: Session): void + 'onebot/channel-created'(session: Session): void + 'onebot/channel-destroyed'(session: Session): void } } diff --git a/adapters/onebot/src/types.ts b/adapters/onebot/src/types.ts index f30a2e9d..6a976fb4 100644 --- a/adapters/onebot/src/types.ts +++ b/adapters/onebot/src/types.ts @@ -1,4 +1,4 @@ -import { camelize, Dict, Logger, Universal } from '@satorijs/satori' +import { camelize, Dict, Logger } from '@satorijs/satori' import { CQCode } from './bot' export interface Response { @@ -89,11 +89,6 @@ export interface UnidirectionalFriendInfo extends AccountInfo { source: string } -export interface Author extends Universal.Author { - time?: string | number - messageId?: string -} - export interface GroupBase { group_id: number group_name: string diff --git a/adapters/onebot/src/utils.ts b/adapters/onebot/src/utils.ts index b253e6c2..c5b54d2e 100644 --- a/adapters/onebot/src/utils.ts +++ b/adapters/onebot/src/utils.ts @@ -7,7 +7,7 @@ export * from './types' const logger = new Logger('onebot') -export const adaptUser = (user: OneBot.AccountInfo): Universal.User => ({ +export const decodeUser = (user: OneBot.AccountInfo): Universal.User => ({ id: user.tiny_id || user.user_id.toString(), name: user.nickname, userId: user.tiny_id || user.user_id.toString(), @@ -15,10 +15,9 @@ export const adaptUser = (user: OneBot.AccountInfo): Universal.User => ({ username: user.nickname, }) -export const adaptGuildMember = (user: OneBot.SenderInfo): Universal.GuildMember => ({ - ...adaptUser(user), - user: adaptUser(user), - nickname: user.card, +export const decodeGuildMember = (user: OneBot.SenderInfo): Universal.GuildMember => ({ + user: decodeUser(user), + name: user.card, roles: [user.role], }) @@ -28,7 +27,7 @@ export const adaptQQGuildMemberInfo = (user: OneBot.GuildMemberInfo): Universal. name: user.nickname, isBot: user.role_name === '机器人', }, - nickname: user.nickname, + name: user.nickname, roles: user.role_name ? [user.role_name] : [], }) @@ -38,34 +37,20 @@ export const adaptQQGuildMemberProfile = (user: OneBot.GuildMemberProfile): Univ name: user.nickname, isBot: user.roles?.some(r => r.role_name === '机器人'), }, - nickname: user.nickname, + name: user.nickname, roles: user.roles?.map(r => r.role_name) || [], }) -export const adaptAuthor = (user: OneBot.SenderInfo, anonymous?: OneBot.AnonymousInfo): Universal.Author => ({ - ...adaptUser(user), - nickname: anonymous?.name || user.card, - anonymous: anonymous?.flag, - roles: [user.role], -}) - -export async function adaptMessage(bot: BaseBot, message: OneBot.Message, result: Universal.Message = {}) { - // basic properties - result.author = adaptAuthor(message.sender, message.anonymous) - result.userId = result.author.userId - result.messageId = message.message_id.toString() - result.timestamp = message.time * 1000 - if (message.guild_id) { - result.guildId = message.guild_id - result.channelId = message.channel_id - } else if (message.group_id) { - result.guildId = result.channelId = message.group_id.toString() - } else { - result.channelId = 'private:' + result.author.userId - } +export async function adaptMessage( + bot: BaseBot, + data: OneBot.Message, + message: Universal.Message = {}, + payload: Universal.Message | Universal.EventData = message, +) { + message.id = message.messageId = data.message_id.toString() // message content - const chain = CQCode.parse(message.message) + const chain = CQCode.parse(data.message) if (bot.config.advanced.splitMixedContent) { chain.forEach((item, index) => { if (item.type !== 'image') return @@ -80,7 +65,7 @@ export async function adaptMessage(bot: BaseBot, message: OneBot.Message, result }) } - result.elements = h.transform(chain, { + message.elements = h.transform(chain, { at({ qq }) { if (qq !== 'all') return h.at(qq) return h('at', { type: 'all' }) @@ -95,16 +80,32 @@ export async function adaptMessage(bot: BaseBot, message: OneBot.Message, result return h('audio', attrs) }, }) - if (result.elements[0]?.type === 'reply') { - const reply = result.elements.shift() - result.quote = await bot.getMessage(result.channelId, reply.attrs.id).catch((error) => { + const [guildId, channelId] = decodeGuildChannelId(data) + if (message.elements[0]?.type === 'reply') { + const reply = message.elements.shift() + message.quote = await bot.getMessage(channelId, reply.attrs.id).catch((error) => { logger.warn(error) return undefined }) } + message.content = message.elements.join('') + + if (!payload) return message + payload.user = decodeUser(data.sender) + payload.member = decodeGuildMember(data.sender) + payload.timestamp = data.time * 1000 + payload.guild = guildId && { id: guildId } + payload.channel = channelId && { id: channelId, type: guildId ? Universal.Channel.Type.TEXT : Universal.Channel.Type.DIRECT } +} - result.content = result.elements.join('') - return result +const decodeGuildChannelId = (data: OneBot.Message) => { + if (data.guild_id) { + return [data.guild_id, data.channel_id] + } else if (data.group_id) { + return [data.group_id.toString(), data.group_id.toString()] + } else { + return [undefined, 'private:' + data.sender.user_id] + } } export const adaptGuild = (info: OneBot.GroupInfo | OneBot.GuildBaseInfo): Universal.Guild => { @@ -129,16 +130,14 @@ export const adaptChannel = (info: OneBot.GroupInfo | OneBot.ChannelInfo): Unive return { id: channel.channel_id, name: channel.channel_name, - channelId: channel.channel_id.toString(), - channelName: channel.channel_name, + type: Universal.Channel.Type.TEXT, } } else { const group = info as OneBot.GroupInfo return { id: group.group_id.toString(), name: group.group_name, - channelId: group.group_id.toString(), - channelName: group.group_name, + type: Universal.Channel.Type.TEXT, } } } @@ -163,7 +162,7 @@ export async function adaptSession(bot: BaseBot, data: OneBot.Payload) { session.type = data.post_type if (data.post_type === 'message' || data.post_type === 'message_sent') { - await adaptMessage(bot, data, session) + await adaptMessage(bot, data, session.data.message = {}, session.data) if (data.post_type === 'message_sent' && !session.guildId) { session.channelId = 'private:' + data.target_id } @@ -179,7 +178,7 @@ export async function adaptSession(bot: BaseBot, data: OneBot.Payload) { if (data.group_id) session.guildId = session.channelId = '' + data.group_id if (data.guild_id) session.guildId = '' + data.guild_id if (data.channel_id) session.channelId = '' + data.channel_id - if (data.target_id) session.targetId = '' + data.target_id + if (data.target_id) session['targetId'] = '' + data.target_id if (data.operator_id) session.operatorId = '' + data.operator_id if (data.message_id) session.messageId = '' + data.message_id