From c790afae9b4d248056e6f831043b3bf9a0fcc589 Mon Sep 17 00:00:00 2001 From: Shigma Date: Fri, 8 Jul 2022 02:24:16 +0800 Subject: [PATCH] feat(qqguild): support qqguild adapter --- adapters/qqguild/package.json | 36 ++++++++++++++ adapters/qqguild/src/bot.ts | 90 ++++++++++++++++++++++++++++++++++ adapters/qqguild/src/index.ts | 16 ++++++ adapters/qqguild/src/utils.ts | 15 ++++++ adapters/qqguild/src/ws.ts | 31 ++++++++++++ adapters/qqguild/tsconfig.json | 10 ++++ package.json | 2 +- 7 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 adapters/qqguild/package.json create mode 100644 adapters/qqguild/src/bot.ts create mode 100644 adapters/qqguild/src/index.ts create mode 100644 adapters/qqguild/src/utils.ts create mode 100644 adapters/qqguild/src/ws.ts create mode 100644 adapters/qqguild/tsconfig.json diff --git a/adapters/qqguild/package.json b/adapters/qqguild/package.json new file mode 100644 index 00000000..08c01922 --- /dev/null +++ b/adapters/qqguild/package.json @@ -0,0 +1,36 @@ +{ + "name": "@satorijs/adapter-qqguild", + "description": "QQ Guild Adapter for Satorijs", + "version": "1.0.0", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "files": [ + "lib" + ], + "author": "Shigma ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/satorijs/node-sdk.git", + "directory": "adapters/qqguild" + }, + "bugs": { + "url": "https://github.com/satorijs/node-sdk/issues" + }, + "homepage": "https://koishi.js.org/api/adapter/qqguild.html", + "keywords": [ + "bot", + "qq", + "qqguild", + "chatbot", + "satori" + ], + "peerDependencies": { + "@satorijs/core": "^1.0.0", + "@satorijs/env-node": "^1.0.0" + }, + "dependencies": { + "@qq-guild-sdk/core": "^2.0.1", + "qface": "^1.2.0" + } +} \ No newline at end of file diff --git a/adapters/qqguild/src/bot.ts b/adapters/qqguild/src/bot.ts new file mode 100644 index 00000000..93da1659 --- /dev/null +++ b/adapters/qqguild/src/bot.ts @@ -0,0 +1,90 @@ +import * as QQGuild from '@qq-guild-sdk/core' +import { Bot, Context, Session } from '@satorijs/core' +import { Schema } from '@satorijs/env-node' +import { adaptGuild, adaptUser } from './utils' +import { WsClient } from './ws' +import segment from '@satorijs/message' + +export class QQGuildBot extends Bot { + internal: QQGuild.Bot + + constructor(ctx: Context, config: QQGuildBot.Config) { + super(ctx, config) + this.internal = new QQGuild.Bot(config) + } + + async getSelf() { + const user = adaptUser(await this.internal.me) + user['selfId'] = user.userId + delete user.userId + return user + } + + async sendMessage(channelId: string, content: string, guildId?: string) { + const session = await this.session({ channelId, content, guildId, subtype: 'group' }) + if (!session?.content) return [] + const resp = await this.internal.send.channel(channelId, session.content) + session.messageId = resp.id + this.ctx.emit(session, 'send', session) + this.ctx.emit(session, 'message', this.adaptMessage(resp)) + return [resp.id] + } + + async getGuildList() { + return this.internal.guilds.then(guilds => guilds.map(adaptGuild)) + } + + adaptMessage(msg: QQGuild.Message) { + const { + id: messageId, author, guildId, channelId, timestamp, + } = msg + const session: Partial = { + selfId: this.selfId, + guildId, + messageId, + channelId, + timestamp: +timestamp, + } + session.author = adaptUser(msg.author) + session.userId = author.id + session.guildId = msg.guildId + session.channelId = msg.channelId + session.subtype = 'group' + session.content = (msg.content ?? '') + .replace(/<@!(.+)>/, (_, $1) => segment.at($1)) + .replace(/<#(.+)>/, (_, $1) => segment.sharp($1)) + const { attachments = [] } = msg as any as { attachments?: any[] } + if (attachments.length > 0) { + session.content += attachments.map((attachment) => { + if (attachment.contentType.startsWith('image')) { + return segment.image(attachment.url) + } + }).join('') + } + session.content = attachments + .filter(({ contentType }) => contentType.startsWith('image')) + .reduce((content, attachment) => content + segment.image(attachment.url), session.content) + return new Session(this, session) + } +} + +export namespace QQGuildBot { + export interface Config extends Bot.Config, QQGuild.Bot.Options, WsClient.Config { + intents: number + } + + export const Config: Schema = Schema.intersect([ + Schema.object({ + app: Schema.object({ + id: Schema.string().description('机器人 id。').required(), + key: Schema.string().description('机器人 key。').role('secret').required(), + token: Schema.string().description('机器人令牌。').role('secret').required(), + }), + sandbox: Schema.boolean().description('是否开启沙箱模式。').default(true), + endpoint: Schema.string().role('url').description('API 入口地址。').default('https://api.sgroup.qq.com/'), + authType: Schema.union(['bot', 'bearer'] as const).description('采用的验证方式。').default('bot'), + intents: Schema.bitset(QQGuild.Bot.Intents).description('需要订阅的机器人事件。').default(QQGuild.Bot.Intents.PUBLIC_GUILD_MESSAGES), + }), + WsClient.Config, + ] as const) +} diff --git a/adapters/qqguild/src/index.ts b/adapters/qqguild/src/index.ts new file mode 100644 index 00000000..6e72704e --- /dev/null +++ b/adapters/qqguild/src/index.ts @@ -0,0 +1,16 @@ +import * as QQGuild from '@qq-guild-sdk/core' +import { QQGuildBot } from './bot' + +export { QQGuild } + +export * from './bot' +export * from './utils' +export * from './ws' + +export default QQGuildBot + +declare module '@satorijs/core' { + interface Session { + qqguild?: QQGuild.Bot + } +} diff --git a/adapters/qqguild/src/utils.ts b/adapters/qqguild/src/utils.ts new file mode 100644 index 00000000..242d34bb --- /dev/null +++ b/adapters/qqguild/src/utils.ts @@ -0,0 +1,15 @@ +import { Guild, User } from '@satorijs/core' +import * as QQGuild from '@qq-guild-sdk/core' + +export const adaptGuild = (guild: QQGuild.Guild): Guild => ({ + guildId: guild.id, + guildName: guild.name, +}) + +export const adaptUser = (user: QQGuild.User): User => ({ + isBot: user.bot, + avatar: user.avatar, + userId: user.id, + username: user.username, + nickname: user.username, +}) diff --git a/adapters/qqguild/src/ws.ts b/adapters/qqguild/src/ws.ts new file mode 100644 index 00000000..bfb2fc49 --- /dev/null +++ b/adapters/qqguild/src/ws.ts @@ -0,0 +1,31 @@ +import { Adapter } from '@satorijs/core' +import { Schema } from '@satorijs/env-node' +import { QQGuildBot } from './bot' + +export class WsClient extends Adapter { + async start(bot: QQGuildBot) { + Object.assign(bot, await bot.getSelf()) + await bot.internal.startClient(bot.config.intents) + bot.internal.on('ready', bot.online.bind(bot)) + bot.internal.on('message', msg => { + const session = bot.adaptMessage(msg) + if (session) { + session.type = 'message' + bot.dispatch(session) + } + }) + } + + async stop(bot: QQGuildBot) { + bot.internal.stopClient() + bot.offline() + } +} + +export namespace WsClient { + export interface Config extends Adapter.WsClient.Config {} + + export const Config: Schema = Schema.intersect([ + Adapter.WsClient.Config, + ]) +} diff --git a/adapters/qqguild/tsconfig.json b/adapters/qqguild/tsconfig.json new file mode 100644 index 00000000..74ac2c8d --- /dev/null +++ b/adapters/qqguild/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + }, + "include": [ + "src", + ], +} \ No newline at end of file diff --git a/package.json b/package.json index 784131a6..5a26d108 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "yakumo": "^0.2.8", "yakumo-mocha": "^0.2.5", "yakumo-publish": "^0.2.4", - "yakumo-tsc": "^0.2.2", + "yakumo-tsc": "^0.2.3", "yakumo-upgrade": "^0.2.3", "yakumo-version": "^0.2.5" },