first commit

This commit is contained in:
sliverp
2026-01-28 17:18:41 +08:00
commit 9a531cd6eb
10 changed files with 778 additions and 0 deletions

116
src/api.ts Normal file
View File

@@ -0,0 +1,116 @@
/**
* QQ Bot API 鉴权和请求封装
*/
const API_BASE = "https://api.sgroup.qq.com";
const TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
let cachedToken: { token: string; expiresAt: number } | null = null;
/**
* 获取 AccessToken带缓存
*/
export async function getAccessToken(appId: string, clientSecret: string): Promise<string> {
// 检查缓存,提前 5 分钟刷新
if (cachedToken && Date.now() < cachedToken.expiresAt - 5 * 60 * 1000) {
return cachedToken.token;
}
const response = await fetch(TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ appId, clientSecret }),
});
const data = (await response.json()) as { access_token?: string; expires_in?: number };
if (!data.access_token) {
throw new Error(`Failed to get access_token: ${JSON.stringify(data)}`);
}
cachedToken = {
token: data.access_token,
expiresAt: Date.now() + (data.expires_in ?? 7200) * 1000,
};
return cachedToken.token;
}
/**
* 清除 Token 缓存
*/
export function clearTokenCache(): void {
cachedToken = null;
}
/**
* API 请求封装
*/
export async function apiRequest<T = unknown>(
accessToken: string,
method: string,
path: string,
body?: unknown
): Promise<T> {
const url = `${API_BASE}${path}`;
const options: RequestInit = {
method,
headers: {
Authorization: `QQBot ${accessToken}`,
"Content-Type": "application/json",
},
};
if (body) {
options.body = JSON.stringify(body);
}
const res = await fetch(url, options);
const data = (await res.json()) as T;
if (!res.ok) {
const error = data as { message?: string; code?: number };
throw new Error(`API Error [${path}]: ${error.message ?? JSON.stringify(data)}`);
}
return data;
}
/**
* 获取 WebSocket Gateway URL
*/
export async function getGatewayUrl(accessToken: string): Promise<string> {
const data = await apiRequest<{ url: string }>(accessToken, "GET", "/gateway");
return data.url;
}
/**
* 发送 C2C 单聊消息
*/
export async function sendC2CMessage(
accessToken: string,
openid: string,
content: string,
msgId?: string
): Promise<{ id: string; timestamp: number }> {
return apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, {
content,
msg_type: 0,
...(msgId ? { msg_id: msgId } : {}),
});
}
/**
* 发送频道消息
*/
export async function sendChannelMessage(
accessToken: string,
channelId: string,
content: string,
msgId?: string
): Promise<{ id: string; timestamp: string }> {
return apiRequest(accessToken, "POST", `/channels/${channelId}/messages`, {
content,
...(msgId ? { msg_id: msgId } : {}),
});
}