Skip to content

Commit

Permalink
refactor(qqguild): events
Browse files Browse the repository at this point in the history
  • Loading branch information
XxLittleCxX committed Sep 15, 2023
1 parent 61c3dcf commit 6e82ae7
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 76 deletions.
40 changes: 6 additions & 34 deletions adapters/qqguild/src/bot.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Bot, Context, defineProperty, Fragment, h, Quester, Schema, SendOptions } from '@satorijs/satori'
import { adaptGuild, adaptUser } from './utils'
import { Bot, Context, defineProperty, Quester, Schema, Universal } from '@satorijs/satori'
import { adaptUser, decodeMessage } from './utils'
import { QQGuildMessageEncoder } from './message'
import { WsClient } from './ws'
import { Internal } from './internal'
Expand Down Expand Up @@ -45,38 +45,9 @@ export class QQGuildBot extends Bot<QQGuildBot.Config> {
// const guilds = await this.internal.guilds
// return { data: guilds.map(adaptGuild) }
// }

adaptMessage(msg: QQGuild.Message
, input?: QQGuild.Payload,
) {
const { id: messageId, author, guild_id, channel_id, timestamp } = msg
const session = this.session({
type: 'message',
guildId: guild_id,
messageId,
channelId: channel_id,
timestamp: new Date(timestamp).valueOf(),
}
, input,
)
session.author = adaptUser(msg.author)
session.userId = author.id
if (msg.direct_message) {
session.guildId = msg.src_guild_id
} else {
session.guildId = guild_id
session.channelId = channel_id
}
session.isDirect = !!msg.direct_message
session.content = (msg.content ?? '')
.replace(/<@!(.+)>/, (_, $1) => h.at($1).toString())
.replace(/<#(.+)>/, (_, $1) => h.sharp($1).toString())
const { attachments = [] } = msg as { attachments?: any[] }
session.content = attachments
.filter(({ contentType }) => contentType.startsWith('image'))
.reduce((content, attachment) => content + h.image(attachment.url), session.content)
session.elements = h.parse(session.content)
return session
async getMessage(channelId: string, messageId: string): Promise<Universal.Message> {
const r = await this.internal.getMessage(channelId, messageId)
return decodeMessage(this, r)
}
}

Expand All @@ -93,6 +64,7 @@ export namespace QQGuildBot {
id: Schema.string().description('机器人 id。').required(),
key: Schema.string().description('机器人 key。').role('secret').required(),
token: Schema.string().description('机器人令牌。').role('secret').required(),
type: Schema.union(['public', 'private'] as const).description('机器人类型。').required(),
}) as any,
sandbox: Schema.boolean().description('是否开启沙箱模式。').default(true),
endpoint: Schema.string().role('link').description('要连接的服务器地址。').default('https://api.sgroup.qq.com/'),
Expand Down
11 changes: 9 additions & 2 deletions adapters/qqguild/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Quester } from '@satorijs/satori'
import { DMS, User } from './types'
import { DMS, Message, User } from './types'

export class Internal {
constructor(private http: Quester) {}
constructor(private http: Quester) { }

async getMe() {
return this.http.get<User>('/users/@me')
Expand All @@ -14,4 +14,11 @@ export class Internal {
recipient_id, source_guild_id,
})
}

async getMessage(channelId: string, messageId: string) {
const { message } = await this.http.get<{
message: Message
}>(`/channels/${channelId}/messages/${messageId}`)
return message
}
}
11 changes: 5 additions & 6 deletions adapters/qqguild/src/message.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { fileURLToPath } from 'url'
import * as QQGuild from './types'
import { Dict, h, Logger, MessageEncoder, Quester } from '@satorijs/satori'
import { Dict, h, MessageEncoder } from '@satorijs/satori'
import { QQGuildBot } from './bot'
import FormData from 'form-data'

const logger = new Logger('satori')
import { decodeMessage } from './utils'

export class QQGuildMessageEncoder extends MessageEncoder<QQGuildBot> {
private mode: 'figure' | 'default' = 'default'
// private mode: 'figure' | 'default' = 'default'
private content: string = ''
private file: Buffer
private filename: string
Expand Down Expand Up @@ -51,7 +49,8 @@ export class QQGuildMessageEncoder extends MessageEncoder<QQGuildBot> {
const r = await this.bot.http.post(endpoint, form, {
headers: form.getHeaders(),
})
const session = this.bot.adaptMessage(r)
const session = this.bot.session()
await decodeMessage(this.bot, r, session)

// https://bot.q.qq.com/wiki/develop/api/gateway/direct_message.html#%E6%B3%A8%E6%84%8F
// session.guildId = this.session.guildId
Expand Down
65 changes: 38 additions & 27 deletions adapters/qqguild/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export enum Opcode {
HEARTBEAT_ACK = 11
}

type DispatchPayload = {
export type DispatchPayload = {
op: Opcode.DISPATCH
s: number
t: 'READY'
Expand Down Expand Up @@ -171,6 +171,16 @@ export type Payload = DispatchPayload | {
}
}

export interface Attachment {
content_type: string
filename: string
height: number
id: string
size: number
url: string
width: number
}

export interface Message {
/** 消息 id */
id: string
Expand All @@ -188,8 +198,8 @@ export interface Message {
edited_timestamp: string
/** 是否是@全员消息 */
mention_everyone: boolean
// /** 附件 */
// attachments: Attachment
/** 附件 */
attachments: Attachment[]
/** embed */
embeds: Message.Embed[]
/** 消息中@的人 */
Expand Down Expand Up @@ -240,11 +250,11 @@ export namespace Message {
/** 描述 */
description: string
/** 消息弹窗内容 */
prompt: string
prompt: string
/** 消息创建时间 */
timestamp: Date
/** 对象数组 消息创建时间 */
fields: EmbedField
/** 对象数组 消息创建时间 */
fields: EmbedField
}
export interface Markdown {
/** markdown 模板 id */
Expand All @@ -262,7 +272,7 @@ export namespace Message {
}
export interface Reference {
/** 需要引用回复的消息 id */
messageId: string
message_id: string
/** 是否忽略获取引用消息详情错误,默认否 */
ignoreGetMessageError?: boolean
}
Expand Down Expand Up @@ -389,9 +399,9 @@ export enum ChannelSubType {

export interface ChannelPermissions {
/** 子频道 id */
channelId: string
channel_id: string
/** 用户 id */
userId: string
user_id: string
/** 用户拥有的子频道权限 */
permissions: string
}
Expand All @@ -418,7 +428,7 @@ export interface Channel {
/** 子频道 id */
id: string
/** 频道 id */
guildId: string
guild_id: string
/** 子频道名 */
name: string
/** 子频道类型 */
Expand All @@ -443,7 +453,7 @@ export interface Channel {

export interface MemberWithGuild {
/** 频道 id */
guildId: string
guild_id: string
/** 用户基础信息 */
user: User
/** 用户在频道内的昵称 */
Expand All @@ -459,9 +469,9 @@ export interface MemberWithGuild {
*/
export interface Announce {
/** 频道 id */
guildId: string
guild_id: string
/** 子频道 id */
channelId: string
channel_id: string
/** 消息 id */
messageId: string
}
Expand All @@ -471,11 +481,11 @@ export interface Announce {
*/
export interface MessageReaction {
/** 用户 ID */
userId: string
user_id: string
/** 频道 ID */
guildId: string
guild_id: string
/** 子频道 ID */
channelId: string
channel_id: string
/** 表态对象 */
target: ReactionTarget
/** 表态所用表情 */
Expand All @@ -497,13 +507,13 @@ export interface ReactionTarget {
*/
export enum ReactionTargetType {
/** 消息 */
MESSAGE = 0,
MESSAGE = 'ReactionTargetType_MSG',
/** 帖子 */
POST = 1,
POST = 'ReactionTargetType_FEED',
/** 评论 */
COMMENT = 2,
COMMENT = 'ReactionTargetType_COMMNENT',
/** 回复 */
REPLY = 3
REPLY = 'ReactionTargetType_REPLY'
}

/**
Expand Down Expand Up @@ -547,7 +557,7 @@ export interface Schedule {
/** 创建者 */
creator: Member
/** 日程开始时跳转到的子频道 id */
jumpChannelId: string
jumpchannel_id: string
/** 日程提醒类型,取值参考 RemindType */
remindType: RemindType
}
Expand Down Expand Up @@ -576,7 +586,7 @@ export interface Mute {
/** 禁言多少秒(两个字段二选一,默认以 muteEndTimestamp 为准) */
muteSeconds?: number
/** 禁言成员的user_id列表,即 User 的id */
userIds?: string[]
user_ids?: string[]
}

export enum DeleteHistoryMsgDays {
Expand All @@ -594,7 +604,7 @@ export interface MessageSetting {
/** 是否允许发主动消息 */
disablePushMsg: string
/** 子频道 id 数组 */
channelIds: string
channel_ids: string
/** 每个子频道允许主动推送消息最大消息条数 */
channelPushMaxNum: string
}
Expand All @@ -616,9 +626,9 @@ export interface DMS {
*/
export interface PinsMessage {
/** 频道 id */
guildId: string
guild_id: string
/** 子频道 id */
channelId: string
channel_id: string
/** 子频道内精华消息 id 数组 */
messageIds: string[]
}
Expand Down Expand Up @@ -649,9 +659,9 @@ export interface APIPermissionDemandIdentify {
*/
export interface APIPermissionDemand {
/** 申请接口权限的频道 id */
guildId: string
guild_id: string
/** 接口权限需求授权链接发送的子频道 id */
channelId: string
channel_id: string
/** 权限接口唯一标识 */
apiIdentify: APIPermissionDemandIdentify
/** 接口权限链接中的接口权限描述信息 */
Expand All @@ -664,6 +674,7 @@ export interface AppConfig {
id: string
key: string
token: string
type: 'public' | 'private'
}

export interface Options {
Expand Down
82 changes: 81 additions & 1 deletion adapters/qqguild/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Universal } from '@satorijs/satori'
import { h, Session, Universal } from '@satorijs/satori'
import * as QQGuild from './types'
import { QQGuildBot } from './bot'

export const adaptGuild = (guild: QQGuild.Guild): Universal.Guild => ({
id: guild.id,
Expand All @@ -14,3 +15,82 @@ export const adaptUser = (user: QQGuild.User): Universal.User => ({
isBot: user.bot,
avatar: user.avatar,
})

export async function decodeMessage(bot: QQGuildBot, msg: QQGuild.Message, session: Partial<Session> = {}): Promise<Universal.Message> {
const { id: messageId, author, guild_id, channel_id, timestamp } = msg
session.type = 'message'
session.guildId = guild_id
session.messageId = messageId
session.channelId = channel_id
session.timestamp = new Date(timestamp).valueOf()

session.author = adaptUser(msg.author)
session.userId = author.id
if (msg.direct_message) {
session.guildId = msg.src_guild_id
} else {
session.guildId = guild_id
session.channelId = channel_id
}
session.isDirect = !!msg.direct_message
session.content = (msg.content ?? '')
.replace(/<@!(.+)>/, (_, $1) => h.at($1).toString())
.replace(/<#(.+)>/, (_, $1) => h.sharp($1).toString())
const { attachments = [] } = msg
session.content = attachments
.filter(({ content_type }) => content_type.startsWith('image'))
.reduce((content, attachment) => content + h.image('https://' + attachment.url), session.content)
session.elements = h.parse(session.content)

if (msg.message_reference) {
session.quote = await bot.getMessage(msg.channel_id, msg.message_reference.message_id)
}

return session
}

export function setupReaction(session: Partial<Session>, data: QQGuild.MessageReaction) {
session.userId = data.user_id
session.guildId = data.guild_id
session.channelId = data.channel_id
session.content = `${data.emoji.type}:${data.emoji.id}`
// https://bot.q.qq.com/wiki/develop/api/openapi/reaction/model.html#reactiontargettype
session.messageId = data.target.id
session.isDirect = false
// @TODO type
return session
}

export async function adaptSession(bot: QQGuildBot, input: QQGuild.DispatchPayload) {
const session = bot.session({}, input)
if (input.t === 'MESSAGE_CREATE' || input.t === 'AT_MESSAGE_CREATE' || input.t === 'DIRECT_MESSAGE_CREATE') {
if (bot.config.app.type === 'private' && input.t === 'AT_MESSAGE_CREATE') return
await decodeMessage(bot, input.d, session)
} else if (input.t === 'MESSAGE_REACTION_ADD') {
setupReaction(session, input.d)
session.type = 'reaction-added'
} else if (input.t === 'MESSAGE_REACTION_REMOVE') {
setupReaction(session, input.d)
session.type = 'reaction-removed'
} else if (input.t === 'CHANNEL_CREATE' || input.t === 'CHANNEL_UPDATE' || input.t === 'CHANNEL_DELETE') {
session.type = {
CHANNEL_CREATE: 'channel-added',
CHANNEL_UPDATE: 'channel-updated',
CHANNEL_DELETE: 'channel-deleted',
}[input.t]
session.guildId = input.d.guild_id
session.channelId = input.d.id
session.channelName = input.d.name
} else if (input.t === 'GUILD_CREATE' || input.t === 'GUILD_UPDATE' || input.t === 'GUILD_DELETE') {
session.type = {
GUILD_CREATE: 'guild-added',
GUILD_UPDATE: 'guild-updated',
GUILD_DELETE: 'guild-deleted',
}[input.t]
session.guildId = input.d.id
session.guildName = input.d.name
} else {
return
}
return session
}
Loading

0 comments on commit 6e82ae7

Please sign in to comment.