UNPKG

amesu

Version:
238 lines (237 loc) 8.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Session = void 0; const ws_1 = require("ws"); const node_events_1 = require("node:events"); const logger_1 = require("../utils/logger"); const common_1 = require("../utils/common"); var OpCode; (function (OpCode) { /** 服务端进行消息推送 */ OpCode[OpCode["Dispatch"] = 0] = "Dispatch"; /** 客户端或服务端发送心跳 */ OpCode[OpCode["Heartbeat"] = 1] = "Heartbeat"; /** 客户端发送鉴权 */ OpCode[OpCode["Identify"] = 2] = "Identify"; /** 客户端恢复连接 */ OpCode[OpCode["Resume"] = 6] = "Resume"; /** 服务端通知客户端重新连接 */ OpCode[OpCode["Reconnect"] = 7] = "Reconnect"; /** 当 Identify 或 Resume 的时候,如果参数有错,服务端会返回该消息 */ OpCode[OpCode["InvalidSession"] = 9] = "InvalidSession"; /** 当客户端与网关建立 ws 连接之后,网关下发的第一条消息 */ OpCode[OpCode["Hello"] = 10] = "Hello"; /** 当发送心跳成功之后,就会收到该消息 */ OpCode[OpCode["HeartbeatAck"] = 11] = "HeartbeatAck"; /** 仅用于 http 回调模式的回包,代表机器人收到了平台推送的数据 */ OpCode[OpCode["HttpCallbackAck"] = 12] = "HttpCallbackAck"; })(OpCode || (OpCode = {})); var DispatchType; (function (DispatchType) { DispatchType["READY"] = "READY"; DispatchType["RESUMED"] = "RESUMED"; })(DispatchType || (DispatchType = {})); /** 事件类型 */ var Intent; (function (Intent) { Intent[Intent["GUILDS"] = 1] = "GUILDS"; Intent[Intent["GUILD_MEMBERS"] = 2] = "GUILD_MEMBERS"; Intent[Intent["GUILD_MESSAGES"] = 512] = "GUILD_MESSAGES"; Intent[Intent["GUILD_MESSAGE_REACTIONS"] = 1024] = "GUILD_MESSAGE_REACTIONS"; Intent[Intent["DIRECT_MESSAGE"] = 4096] = "DIRECT_MESSAGE"; Intent[Intent["GROUP_AND_C2C_EVENT"] = 33554432] = "GROUP_AND_C2C_EVENT"; Intent[Intent["INTERACTION"] = 67108864] = "INTERACTION"; Intent[Intent["MESSAGE_AUDIT"] = 134217728] = "MESSAGE_AUDIT"; Intent[Intent["FORUMS_EVENT"] = 268435456] = "FORUMS_EVENT"; Intent[Intent["AUDIO_ACTION"] = 536870912] = "AUDIO_ACTION"; Intent[Intent["PUBLIC_GUILD_MESSAGES"] = 1073741824] = "PUBLIC_GUILD_MESSAGES"; })(Intent || (Intent = {})); class SessionError extends Error { constructor(message) { super(message); this.name = 'SessionError'; } } class Session extends node_events_1.EventEmitter { config; token; ackTimeout; /** 心跳间隔 */ heartbeat_interval; /** 是否重连 */ is_reconnect; /** 记录器 */ logger; /** 重连计数 */ retry; /** 最大重连数 */ max_retry; /** 消息序列号 */ seq; /** 会话 id */ session_id; ws; constructor(config, token) { super(); this.config = config; this.token = token; this.ackTimeout = null; this.is_reconnect = false; this.logger = (0, logger_1.getLogger)(config.appid); this.retry = 0; this.max_retry = config.max_retry; this.seq = 0; this.session_id = null; this.ws = null; } onOpen() { if (this.retry) { this.retry = 0; } this.logger.debug('连接 socket 成功'); } async onClose(code) { clearTimeout(this.ackTimeout); this.ackTimeout = null; this.ws.removeAllListeners(); this.logger.debug(`Session Exit Code: ${code}.`); if (!this.is_reconnect) { this.ws = null; this.logger.info('会话连接已关闭'); return; } this.logger.warn('会话连接已被中断'); await this.token.renew(); this.reconnect(); } onError(error) { this.logger.fatal(error); } onMessage(data) { const payload = JSON.parse(data.toString()); this.logger.debug(`收到 payload 数据: ${(0, common_1.objectToString)(payload)}`); switch (payload.op) { case OpCode.Dispatch: this.onDispatch(payload); break; case OpCode.Reconnect: this.logger.info('当前会话已失效,等待断开后自动重连'); this.ws.close(); break; case OpCode.InvalidSession: this.logger.error('发送的 payload 参数有误'); throw new SessionError('The Payload parameter sent is incorrect.'); case OpCode.Hello: this.heartbeat_interval = payload.d.heartbeat_interval; this.is_reconnect ? this.sendResumePayload() : this.sendAuthPayload(); break; case OpCode.HeartbeatAck: this.ackTimeout = setTimeout(() => this.heartbeat(), this.heartbeat_interval); break; } } onDispatch(payload) { const { d, s, t } = payload; this.seq = s; switch (t) { case DispatchType.READY: const { session_id } = d; this.session_id = session_id; this.logger.mark(`Hello, ${d.user.username}`); case DispatchType.RESUMED: this.logger.trace('开始发送心跳...'); this.heartbeat(); break; } const dispatch = { t, d, }; this.emit('dispatch', dispatch); } heartbeat() { const payload = { op: OpCode.Heartbeat, d: this.seq, }; this.sendPayload(payload); } sendPayload(payload) { try { const data = (0, common_1.objectToString)(payload); this.ws.send(data); this.logger.debug(`发送 payload 数据: ${data}`); } catch (error) { this.logger.error(error); } } getIntents() { const events = this.config.events; const intents = events.reduce((previous, current) => previous | Intent[current], 0); return intents; } sendAuthPayload() { const payload = { op: OpCode.Identify, d: { token: this.token.authorization, intents: this.getIntents(), shard: this.config.shard, // TODO: /人◕ ‿‿ ◕人\ properties: {}, }, }; this.is_reconnect = true; this.sendPayload(payload); } sendResumePayload() { const payload = { op: OpCode.Resume, d: { token: this.token.authorization, seq: this.seq, session_id: this.session_id, }, }; this.sendPayload(payload); } async reconnect() { if (this.retry === this.max_retry) { this.logger.error('重连失败,请检查网络和配置。'); throw new SessionError('Reached the maximum number of reconnection attempts.'); } this.retry++; try { this.logger.info(`尝试重连... x${this.retry}`); await (0, common_1.sleep)(this.retry * 3000); this.connect(this.ws.url); } catch (error) { this.reconnect(); } } connect(url) { if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) { this.logger.warn('已建立会话通信,不要重复连接。'); return; } this.logger.trace('开始建立 ws 通信...'); const ws = new ws_1.WebSocket(url); ws.on('open', () => this.onOpen()); ws.on('close', code => this.onClose(code)); ws.on('error', error => this.onError(error)); ws.on('message', data => this.onMessage(data)); this.ws = ws; } disconnect() { if (!this.ws) { this.logger.warn('未建立会话通信,无效的操作。'); return; } this.is_reconnect = false; this.logger.trace('正在断开 ws 通信...'); this.ws.close(); } } exports.Session = Session;