mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-18 20:30:08 +08:00
* feat: pnpm new * Refactor build and release workflows, update dependencies Switch build scripts and workflows from npm to pnpm, update build and artifact paths, and simplify release workflow by removing version detection and changelog steps. Add new dependencies (silk-wasm, express, ws, node-pty-prebuilt-multiarch), update exports in package.json files, and add vite config for napcat-framework. Also, rename manifest.json for framework package and fix static asset copying in shell build config.
131 lines
3.5 KiB
TypeScript
131 lines
3.5 KiB
TypeScript
import { LogWrapper } from 'napcat-common/src/log';
|
||
import { RequestUtil } from 'napcat-common/src/request';
|
||
|
||
interface ServerRkeyData {
|
||
group_rkey: string;
|
||
private_rkey: string;
|
||
expired_time: number;
|
||
}
|
||
interface OneBotApiRet {
|
||
status: string,
|
||
retcode: number,
|
||
data: ServerRkeyData,
|
||
message: string,
|
||
wording: string,
|
||
}
|
||
interface UrlFailureInfo {
|
||
count: number;
|
||
lastTimestamp: number;
|
||
}
|
||
|
||
export class RkeyManager {
|
||
serverUrl: string[] = [];
|
||
logger: LogWrapper;
|
||
private rkeyData: ServerRkeyData = {
|
||
group_rkey: '',
|
||
private_rkey: '',
|
||
expired_time: 0,
|
||
};
|
||
|
||
private urlFailures: Map<string, UrlFailureInfo> = new Map();
|
||
private readonly FAILURE_LIMIT: number = 4;
|
||
private readonly ONE_DAY: number = 24 * 60 * 60 * 1000;
|
||
|
||
constructor (serverUrl: string[], logger: LogWrapper) {
|
||
this.logger = logger;
|
||
this.serverUrl = serverUrl;
|
||
}
|
||
|
||
async getRkey () {
|
||
const availableUrls = this.getAvailableUrls();
|
||
if (availableUrls.length === 0) {
|
||
this.logger.logError('[Rkey] 所有服务均已禁用, 图片使用FallBack机制');
|
||
throw new Error('获取rkey失败:所有服务URL均已被禁用');
|
||
}
|
||
|
||
if (this.isExpired()) {
|
||
try {
|
||
await this.refreshRkey();
|
||
} catch (e) {
|
||
throw new Error(`${e}`);
|
||
}
|
||
}
|
||
return this.rkeyData;
|
||
}
|
||
|
||
private getAvailableUrls (): string[] {
|
||
return this.serverUrl.filter(url => !this.isUrlDisabled(url));
|
||
}
|
||
|
||
private isUrlDisabled (url: string): boolean {
|
||
const failureInfo = this.urlFailures.get(url);
|
||
if (!failureInfo) return false;
|
||
|
||
const now = new Date().getTime();
|
||
// 如果已经过了一天,重置失败计数
|
||
if (now - failureInfo.lastTimestamp > this.ONE_DAY) {
|
||
failureInfo.count = 0;
|
||
this.urlFailures.set(url, failureInfo);
|
||
return false;
|
||
}
|
||
|
||
return failureInfo.count >= this.FAILURE_LIMIT;
|
||
}
|
||
|
||
private updateUrlFailure (url: string) {
|
||
const now = new Date().getTime();
|
||
const failureInfo = this.urlFailures.get(url) || { count: 0, lastTimestamp: 0 };
|
||
|
||
// 如果已经过了一天,重置失败计数
|
||
if (now - failureInfo.lastTimestamp > this.ONE_DAY) {
|
||
failureInfo.count = 1;
|
||
} else {
|
||
failureInfo.count++;
|
||
}
|
||
|
||
failureInfo.lastTimestamp = now;
|
||
this.urlFailures.set(url, failureInfo);
|
||
|
||
if (failureInfo.count >= this.FAILURE_LIMIT) {
|
||
this.logger.logError(`[Rkey] URL ${url} 已被禁用,失败次数达到 ${this.FAILURE_LIMIT} 次`);
|
||
}
|
||
}
|
||
|
||
isExpired (): boolean {
|
||
const now = new Date().getTime() / 1000;
|
||
return now > this.rkeyData.expired_time;
|
||
}
|
||
|
||
async refreshRkey () {
|
||
const availableUrls = this.getAvailableUrls();
|
||
|
||
if (availableUrls.length === 0) {
|
||
this.logger.logError('[Rkey] 所有服务均已禁用');
|
||
throw new Error('获取rkey失败:所有服务URL均已被禁用');
|
||
}
|
||
|
||
for (const url of availableUrls) {
|
||
try {
|
||
let temp = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
|
||
if ('retcode' in temp) {
|
||
// 支持Onebot Ret风格
|
||
temp = (temp as unknown as OneBotApiRet).data;
|
||
}
|
||
this.rkeyData = {
|
||
group_rkey: temp.group_rkey.slice(6),
|
||
private_rkey: temp.private_rkey.slice(6),
|
||
expired_time: temp.expired_time,
|
||
};
|
||
return;
|
||
} catch (e) {
|
||
this.logger.logError(`[Rkey] 异常服务 ${url} 异常 / `, e);
|
||
this.updateUrlFailure(url);
|
||
|
||
if (url === availableUrls[availableUrls.length - 1]) {
|
||
throw new Error(`获取rkey失败: ${e}`);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|