qq-official-bot
Version:
239 lines (238 loc) • 8.46 kB
JavaScript
"use strict";
/**
* 认证管理器 - 负责处理QQ Bot的认证相关功能
* 从SessionManager中提取认证相关功能
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Auth = void 0;
/**
* 认证管理器
* 专门负责处理token获取、刷新和网关信息获取
*/
class Auth {
constructor(config, logger, request) {
this.logger = logger;
this.request = request;
this.config = {
tokenRefreshBuffer: 60, // 提前60秒刷新
maxRetries: 3,
retryDelay: 1000,
...config
};
}
/**
* 获取访问令牌
* 如果当前token有效则返回,否则重新获取
*/
async getAccessToken() {
if (this.isTokenValid()) {
return this.currentToken.access_token;
}
const tokenInfo = await this.fetchNewToken();
this.setToken(tokenInfo);
return tokenInfo.access_token;
}
/**
* 强制刷新访问令牌
*/
async refreshAccessToken() {
this.logger.debug("[AUTH] 强制刷新访问令牌");
const tokenInfo = await this.fetchNewToken();
this.setToken(tokenInfo);
return tokenInfo;
}
/**
* 获取网关连接地址
*/
async getGatewayUrl() {
if (this.gatewayInfo?.url) {
return this.gatewayInfo.url;
}
const gatewayInfo = await this.fetchGatewayInfo();
this.gatewayInfo = gatewayInfo;
return gatewayInfo.url;
}
/**
* 获取完整的网关信息
*/
async getGatewayInfo() {
if (!this.gatewayInfo) {
this.gatewayInfo = await this.fetchGatewayInfo();
}
return this.gatewayInfo;
}
/**
* 从API获取新的访问令牌
*/
async fetchNewToken() {
const { appid, secret } = this.config;
for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {
try {
this.logger.debug(`[AUTH] 获取访问令牌,尝试次数: ${attempt}`);
const response = await this.request.post("https://bots.qq.com/app/getAppAccessToken", {
appId: appid,
clientSecret: secret
}, {
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'QQBot/1.0'
}
});
if (response.status === 200 && response.data?.access_token) {
const tokenInfo = {
access_token: response.data.access_token,
expires_in: response.data.expires_in,
expires_at: Date.now() + (response.data.expires_in * 1000)
};
this.logger.debug("[AUTH] 访问令牌获取成功", {
expires_in: tokenInfo.expires_in,
expires_at: new Date(tokenInfo.expires_at).toISOString()
});
return tokenInfo;
}
else {
throw new Error(`无效的响应: ${response.status} ${JSON.stringify(response.data)}`);
}
}
catch (error) {
this.logger.error(`[AUTH] 获取访问令牌失败 (尝试 ${attempt}/${this.config.maxRetries}):`, error);
if (attempt === this.config.maxRetries) {
throw new Error(`获取访问令牌失败,已重试 ${this.config.maxRetries} 次: ${error}`);
}
// 等待后重试
await this.delay(this.config.retryDelay * attempt);
}
}
throw new Error("获取访问令牌失败");
}
/**
* 从API获取网关信息
*/
async fetchGatewayInfo() {
const token = await this.getAccessToken();
for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {
try {
this.logger.debug(`[AUTH] 获取网关信息,尝试次数: ${attempt}`);
const response = await this.request.get("/gateway/bot", {
headers: {
Accept: "*/*",
"Accept-Encoding": "utf-8",
"Accept-Language": "zh-CN,zh;q=0.8",
Connection: "keep-alive",
"User-Agent": "v1",
Authorization: `QQBot ${token}`
},
timeout: 10000
});
if (response.data?.url) {
const gatewayInfo = {
url: response.data.url,
shards: response.data.shards,
session_start_limit: response.data.session_start_limit
};
this.logger.debug("[AUTH] 网关信息获取成功", gatewayInfo);
return gatewayInfo;
}
else {
throw new Error(`无效的网关响应: ${JSON.stringify(response.data)}`);
}
}
catch (error) {
this.logger.error(`[AUTH] 获取网关信息失败 (尝试 ${attempt}/${this.config.maxRetries}):`, error);
if (attempt === this.config.maxRetries) {
throw new Error(`获取网关信息失败,已重试 ${this.config.maxRetries} 次: ${error}`);
}
await this.delay(this.config.retryDelay * attempt);
}
}
throw new Error("获取网关信息失败");
}
/**
* 设置令牌并启动自动刷新
*/
setToken(tokenInfo) {
this.currentToken = tokenInfo;
this.scheduleTokenRefresh();
this.logger.info("[AUTH] 访问令牌已设置", {
expires_in: tokenInfo.expires_in,
expires_at: tokenInfo.expires_at ? new Date(tokenInfo.expires_at).toISOString() : 'unknown'
});
}
/**
* 计划令牌刷新
*/
scheduleTokenRefresh() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
if (!this.currentToken) {
return;
}
// 计算刷新时间(提前缓冲时间刷新)
const refreshTime = (this.currentToken.expires_in - this.config.tokenRefreshBuffer) * 1000;
if (refreshTime > 0) {
this.refreshTimer = setTimeout(async () => {
try {
this.logger.debug("[AUTH] 自动刷新访问令牌");
await this.refreshAccessToken();
}
catch (error) {
this.logger.error("[AUTH] 自动刷新令牌失败:", error);
// 如果自动刷新失败,可以设置一个较短的重试时间
setTimeout(() => this.scheduleTokenRefresh(), 10000);
}
}, refreshTime);
this.logger.debug(`[AUTH] 令牌刷新已计划,将在 ${refreshTime / 1000} 秒后执行`);
}
}
/**
* 检查当前令牌是否有效
*/
isTokenValid() {
if (!this.currentToken || !this.currentToken.expires_at) {
return false;
}
// 检查是否在缓冲时间内即将过期
const bufferTime = this.config.tokenRefreshBuffer * 1000;
return Date.now() < (this.currentToken.expires_at - bufferTime);
}
/**
* 获取当前令牌信息
*/
getCurrentTokenInfo() {
return this.currentToken ? { ...this.currentToken } : null;
}
/**
* 检查认证状态
*/
isAuthenticated() {
return this.isTokenValid();
}
/**
* 清除认证信息
*/
clearAuth() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
this.refreshTimer = undefined;
}
this.currentToken = undefined;
this.gatewayInfo = undefined;
this.logger.debug("[AUTH] 认证信息已清除");
}
/**
* 工具方法:延迟
*/
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 销毁认证管理器
*/
destroy() {
this.clearAuth();
this.logger.debug("[AUTH] 认证管理器已销毁");
}
}
exports.Auth = Auth;