UNPKG

amesu

Version:
229 lines (228 loc) 8.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = void 0; const node_events_1 = require("node:events"); const api_1 = require("../api"); const token_1 = require("../bot/token"); const session_1 = require("../bot/session"); const common_1 = require("../utils/common"); const request_1 = require("../utils/request"); const logger_1 = require("../utils/logger"); class ClientError extends Error { constructor(message) { super(message); this.name = 'ClientError'; } } class Client extends node_events_1.EventEmitter { config; logger; api; request; token; session; eventInterceptors; constructor(config) { super(); this.config = config; config.sandbox ??= false; config.shard ??= [0, 1]; config.max_retry ??= 3; config.log_level ??= 'INFO'; this.logger = (0, logger_1.createLogger)(config.appid, config.log_level); this.checkConfig(); this.logger.mark('Client is initializing...'); this.api = this.createApi(); this.request = this.createRequest(); this.token = new token_1.Token(config); this.session = new session_1.Session(config, this.token); this.eventInterceptors = []; this.useEventInterceptor(dispatch => { const { t, d } = dispatch; switch (t) { case 'GUILD_MEMBER_ADD': case 'GUILD_MEMBER_UPDATE': case 'GUILD_MEMBER_REMOVE': case 'MESSAGE_REACTION_ADD': case 'MESSAGE_REACTION_REMOVE': case 'FORUM_THREAD_CREATE': case 'FORUM_THREAD_UPDATE': case 'FORUM_THREAD_DELETE': case 'FORUM_POST_CREATE': case 'FORUM_POST_DELETE': case 'FORUM_REPLY_CREATE': case 'FORUM_REPLY_DELETE': d.reply = (params) => { return this.api.sendChannelMessage(d.channel_id, { event_id: d.id, ...params, }); }; break; // case 'INTERACTION_CREATE': case 'GROUP_ADD_ROBOT': case 'GROUP_MSG_RECEIVE': d.reply = (params) => { return this.api.sendGroupMessage(d.group_openid, { event_id: d.id, ...params, }); }; break; case 'MESSAGE_CREATE': case 'AT_MESSAGE_CREATE': d.reply = (params) => { return this.api.sendChannelMessage(d.channel_id, { msg_id: d.id, ...params, }); }; break; case 'DIRECT_MESSAGE_CREATE': d.reply = (params) => { return this.api.sendDmMessage(d.guild_id, { msg_id: d.id, ...params, }); }; break; case 'GROUP_AT_MESSAGE_CREATE': d.reply = (params) => { return this.api.sendGroupMessage(d.group_openid, { msg_id: d.id, ...params, }); }; break; case 'C2C_MESSAGE_CREATE': d.reply = (params) => { return this.api.sendUserMessage(d.author.user_openid, { msg_id: d.id, ...params, }); }; break; } return dispatch; }); } /** * 机器人上线。 */ async online() { const { data } = await this.api.getGateway(); this.session.connect(data.url); this.session.on('dispatch', data => this.onDispatch(data)); this.on('c2c.message.create', this.onMessage); this.on('group.at.message.create', this.onMessage); this.on('direct.message.create', this.onMessage); this.on('at.message.create', this.onMessage); } /** * 机器人下线。 */ offline() { this.session.disconnect(); this.session.removeAllListeners('dispatch'); this.off('c2c.message.create', this.onMessage); this.off('group.at.message.create', this.onMessage); this.off('direct.message.create', this.onMessage); this.off('at.message.create', this.onMessage); } /** * 添加事件拦截器。 */ useEventInterceptor(interceptor) { this.eventInterceptors.push(interceptor); } async onDispatch(dispatch) { for (const interceptor of this.eventInterceptors) { try { dispatch = await interceptor(dispatch); } catch (error) { const message = (0, common_1.parseError)(error); this.logger.error(message); throw new ClientError(message); } } const { t, d } = dispatch; const data = { t, ...d, }; const events = t.toLowerCase().split('_'); // 不存在下划线就是 session 自身的事件,例如 READY、RESUMED if (events.length === 1) { events.unshift('session'); } do { const event = events.join('.'); this.emit(event, data); this.logger.debug(`推送 "${event}" 事件`); events.pop(); } while (events.length); } onMessage(message) { const { attachments, content } = message; const text = attachments ? `<attachment>${content}` : content; this.logger.info(`Received message: "${text}"`); } checkConfig() { if (!this.config.events.length) { const wiki = 'https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html#%E4%BA%8B%E4%BB%B6%E8%AE%A2%E9%98%85Intents'; this.logger.error(`检测到 events 为空,请查阅相关文档:${wiki}`); throw new ClientError('Events cannot be empty.'); } else if (!Array.isArray(this.config.shard) || this.config.shard.length !== 2 || this.config.shard[0] >= this.config.shard[1]) { const wiki = 'https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html'; this.logger.error(`检测到 shard 配置错误,请查阅相关文档:${wiki}`); throw new ClientError('Shard configuration is incorrect.'); } } createApi() { const request = new request_1.Request(); const origin = this.config.sandbox ? 'https://sandbox.api.sgroup.qq.com' : 'https://api.sgroup.qq.com'; request.useRequestInterceptor(async (config) => { this.logger.trace('开始调用接口请求...'); await this.token.renew(); (0, common_1.deepAssign)(config, { origin, headers: { 'Authorization': this.token.authorization, 'X-Union-Appid': this.config.appid, }, }); this.logger.debug(`API Request: ${(0, common_1.objectToString)(config)}`); return config; }); request.useResponseInterceptor(result => { const { data } = result; this.logger.debug(`API Response: ${(0, common_1.objectToString)(data)}`); if (data?.code) { this.logger.error(`API Code: ${data.code}, ${data.message}.`); throw new request_1.RequestError(`Code ${data.code}, ${data.message}.`); } return result; }); return (0, api_1.generateApi)(request); } createRequest() { const request = new request_1.Request(); request.useRequestInterceptor(config => { this.logger.trace('开始发起网络请求...'); this.logger.debug(`HTTP Request: ${(0, common_1.objectToString)(config)}`); return config; }); request.useResponseInterceptor(result => { this.logger.debug(`HTTP Response: ${(0, common_1.objectToString)(result.data)}`); return result; }); return request; } } exports.Client = Client;