@karinjs/adapter-qqbot 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { c as joinHttpPath, d as normalizeWsGatewayUrl, f as pkg, h as basename, l as normalizeHttpUrl, m as log, n as config, r as getConfig, s as buildFallbackWsUrlFromApi, t as bindHandlers, u as normalizeProxyConfig } from "./config-BUuU6FnQ.js";
2
- import { i as random, n as getImageSize, r as handleUrl, t as fakeEvent } from "./common-by1GpV0b.js";
3
- import "./utils-N9PNiHjE.js";
4
- import { t as getMessageStore } from "./message-BTGL3TSn.js";
5
- import { t as needQrOnboard } from "./onboard-vIEoe9dB.js";
6
- import { AdapterBase, app, buttonHandle, common, createDirectMessage, createFriendDecreaseNotice, createFriendIncreaseNotice, createFriendMessage, createGroupMemberAddNotice, createGroupMemberDelNotice, createGroupMessage, createGuildMessage, fileToUrl, fileToUrlHandlerKey, handler, karin as karin$1, karinToQQBot, logger, registerBot, segment, senderGroup, unregisterBot } from "node-karin";
1
+ import { l as basename, n as config, r as getConfig, s as pkg, t as bindHandlers } from "./config-D1_LqrcJ.js";
2
+ import { t as log } from "./logger-sza6kuy3.js";
3
+ import { i as random, n as fakeEvent, r as getImageSize, t as QQAPIBASEURL } from "./utils-QpIUJhxK.js";
4
+ import { t as getMessageStore } from "./message-CVJVFAGR.js";
5
+ import karin, { AdapterBase, app, buttonHandle, common, createDirectMessage, createFriendDecreaseNotice, createFriendIncreaseNotice, createFriendMessage, createGroupApplyRequest, createGroupInviteRequest, createGroupMemberAddNotice, createGroupMemberDelNotice, createGroupMessage, createGuildMessage, fileToUrl, fileToUrlHandlerKey, handler, karin as karin$1, karinToQQBot, logger, registerBot, segment, senderGroup, unregisterBot } from "node-karin";
6
+ import path from "path";
7
7
  import { URL } from "url";
8
8
  import lodash from "node-karin/lodash";
9
9
  import { createReadStream } from "node:fs";
@@ -11,7 +11,7 @@ import FormData from "form-data";
11
11
  import axios from "node-karin/axios";
12
12
  import crypto from "node:crypto";
13
13
  import { open, readFile, stat } from "node:fs/promises";
14
- import path from "node:path";
14
+ import path$1 from "node:path";
15
15
  import { fileURLToPath as fileURLToPath$1 } from "node:url";
16
16
  import os from "node:os";
17
17
  import EventEmitter from "node:events";
@@ -688,7 +688,7 @@ const formatBytes = (bytes) => {
688
688
  const normalizeUploadFileName = (type, name) => {
689
689
  const value = name?.trim();
690
690
  if (!value) return DEFAULT_FILENAME$1[type];
691
- return path.basename(value) || DEFAULT_FILENAME$1[type];
691
+ return path$1.basename(value) || DEFAULT_FILENAME$1[type];
692
692
  };
693
693
  /**
694
694
  * 检查 fallback 资源大小是否超过 QQ 对应类型上限。
@@ -774,7 +774,7 @@ const buildFallbackSource = async (type, source, fileName) => {
774
774
  if (isLocalPathSource(value)) {
775
775
  const file = normalizeLocalPath(value);
776
776
  const info = await stat(file);
777
- const name = normalizeUploadFileName(type, fileName || path.basename(file));
777
+ const name = normalizeUploadFileName(type, fileName || path$1.basename(file));
778
778
  return {
779
779
  kind: "local",
780
780
  file,
@@ -1195,13 +1195,26 @@ var GroupsApi = class extends Http {
1195
1195
  /**
1196
1196
  * 获取群成员详情
1197
1197
  *
1198
- * 未公开文档,来自腾讯官方 openclaw 插件(v2.0.0)的接口速查。
1199
1198
  * @param groupOpenid 群 openid
1200
1199
  * @param memberOpenid 群成员 openid
1201
1200
  */
1202
1201
  getGroupMember(groupOpenid, memberOpenid) {
1203
1202
  return this.get(`/v2/groups/${groupOpenid}/members/${memberOpenid}`);
1204
1203
  }
1204
+ /**
1205
+ * 设置群成员禁言
1206
+ * @param groupOpenid 群 openid
1207
+ * @param memberOpenid 群成员 openid
1208
+ * @param op 操作: add 增加禁言,update 更新禁言到期时间,del 解除禁言
1209
+ * @param duration 禁言时间使用RFC3339 格式
1210
+ */
1211
+ setGroupMetu(groupOpenid, memberOpenid, op, duration) {
1212
+ return this.post(`/v2/groups/${groupOpenid}/restrict_chat_setting`, { members: [{
1213
+ op,
1214
+ member_openid: memberOpenid,
1215
+ mute_expire_at: duration
1216
+ }] });
1217
+ }
1205
1218
  };
1206
1219
 
1207
1220
  //#endregion
@@ -1327,11 +1340,11 @@ const tokens = /* @__PURE__ */ new Map();
1327
1340
  * - 提前 50s 刷新,防止边界过期
1328
1341
  * - 同一 appId 旧定时器会被清理,避免并发刷新链
1329
1342
  */
1330
- const getAccessToken = async (url, appId, secret) => {
1331
- const tokenUrl = normalizeHttpUrl(url);
1343
+ const getAccessToken = async (appId, secret) => {
1332
1344
  let res;
1333
1345
  try {
1334
- res = await axios.post(tokenUrl, {
1346
+ const tokenurl = path.join(QQAPIBASEURL, "/app/getAppAccessToken");
1347
+ res = await axios.post(tokenurl, {
1335
1348
  appId: String(appId),
1336
1349
  clientSecret: secret
1337
1350
  }, { headers: { "User-Agent": getUserAgent() } });
@@ -1353,7 +1366,7 @@ const getAccessToken = async (url, appId, secret) => {
1353
1366
  tokens.set(appId, state);
1354
1367
  const delay = Math.max(5e3, expiresIn * 1e3 - 5e4);
1355
1368
  state.refreshTimer = setTimeout(() => {
1356
- getAccessToken(tokenUrl, appId, secret).catch(() => {});
1369
+ getAccessToken(appId, secret).catch(() => {});
1357
1370
  }, delay);
1358
1371
  return {
1359
1372
  accessToken,
@@ -1378,9 +1391,9 @@ const stopTokenRefresh = (appId) => {
1378
1391
  /**
1379
1392
  * 创建 axios 实例,自动从 tokens map 取最新 access_token
1380
1393
  */
1381
- const createAxiosInstance = (baseURL, appId) => {
1394
+ const createAxiosInstance = (appId) => {
1382
1395
  const instance = axios.create({
1383
- baseURL: normalizeHttpUrl(baseURL),
1396
+ baseURL: QQAPIBASEURL,
1384
1397
  headers: {
1385
1398
  "Content-Type": "application/json",
1386
1399
  "User-Agent": getUserAgent()
@@ -1626,36 +1639,8 @@ const resolvePreferredMediaSource = async (ctx, type, source, filename) => {
1626
1639
 
1627
1640
  //#endregion
1628
1641
  //#region src/core/adapter/text-to-md.ts
1629
- const MAX_BUTTONS = 5;
1630
1642
  const MARKDOWN_IMAGE_RE = /!\[[^\]]*]\(\s*(<[^>]+>|[^\s)]+)(?:\s+["'][^"']*["'])?\s*\)/g;
1631
1643
  /**
1632
- * 提取文本中的 URL,将其替换为 `[按钮 N]` 占位文本并返回 button 列表
1633
- *
1634
- * @param text 文本
1635
- * @param friendly 是否为单聊(true 时清除残余的 at 标签)
1636
- */
1637
- const extractUrlButtons = (text, friendly = false) => {
1638
- let str = text.replace(/@everyone/g, "everyone").replace(/\n/g, "\r");
1639
- if (friendly) str = str.replace(/<qqbot-at-user id="[^"]+"\s*\/?>/g, "").replace(/<@!\d+>/g, "");
1640
- const urls = handleUrl(str);
1641
- if (urls.length === 0) return {
1642
- text: str,
1643
- buttons: []
1644
- };
1645
- const buttons = [];
1646
- urls.forEach((url, i) => {
1647
- str = str.replace(new RegExp(url, "g"), `[请点击 按钮${i} 查看]`);
1648
- buttons.push(segment.button({
1649
- text: `${i}. ${url}`,
1650
- link: url
1651
- }));
1652
- });
1653
- return {
1654
- text: str,
1655
- buttons: buttons.slice(0, MAX_BUTTONS)
1656
- };
1657
- };
1658
- /**
1659
1644
  * 将图片列表合并到一段 markdown 文本内(每张图占一行)
1660
1645
  */
1661
1646
  const imagesToMarkdown = async (urls) => {
@@ -1731,11 +1716,6 @@ const sendQQ = async (ctx, contact, elements) => {
1731
1716
  const target = contact.scene === "friend" ? "user" : "group";
1732
1717
  const grouping = groupElements(contact.scene, elements);
1733
1718
  await resolveOutgoingReferenceQQ(ctx, contact, grouping);
1734
- if (ctx.cfg.markdown.enable && ctx.cfg.keyboard.enable && grouping.text.length) {
1735
- const { text, buttons } = extractUrlButtons(grouping.text.join(""), contact.scene === "friend");
1736
- grouping.text = [text];
1737
- grouping.buttons.push(...buttons);
1738
- }
1739
1719
  /**
1740
1720
  * 自动 Markdown 通道:
1741
1721
  * text / at / 可转换为公网 URL 的图片通过 markdown content 渲染;无法转换的图片
@@ -2089,11 +2069,6 @@ const BUTTON_ONLY_MARKDOWN = "​";
2089
2069
  */
2090
2070
  const sendGuild = async (ctx, contact, elements) => {
2091
2071
  const grouping = groupElements(contact.scene, elements);
2092
- if (ctx.cfg.markdown.enable && ctx.cfg.keyboard.enable && grouping.text.length) {
2093
- const { text, buttons } = extractUrlButtons(grouping.text.join(""), false);
2094
- grouping.text = [text];
2095
- grouping.buttons.push(...buttons);
2096
- }
2097
2072
  if (ctx.cfg.markdown.enable) return sendGuildMarkdown(ctx, contact, grouping);
2098
2073
  return sendGuildClassic(ctx, contact, grouping);
2099
2074
  };
@@ -2176,7 +2151,7 @@ const buildGuildImageItem = async (source) => {
2176
2151
  };
2177
2152
  const resolveImageFilename = (source) => {
2178
2153
  const file = source.split(/[?#]/)[0] || "";
2179
- const name = path.basename(file);
2154
+ const name = path$1.basename(file);
2180
2155
  return name.includes(".") ? name : "image.jpg";
2181
2156
  };
2182
2157
  const buildKeyboard = (grouping) => {
@@ -2220,7 +2195,7 @@ const warnUnsupportedCommandEnterMarkdowns = (ctx, scene, markdowns) => {
2220
2195
  const flushGuild = async (ctx, contact, grouping, items) => {
2221
2196
  const result = ctx.initSendMsgResults();
2222
2197
  const passive = buildPassiveGuild(grouping);
2223
- const send = contact.scene === "guild" ? (peer, subPeer, item) => ctx.super.messages.sendChannelMsg(subPeer, item) : (peer, _subPeer, item) => ctx.super.messages.sendDmsMsg(peer, item);
2198
+ const send = contact.scene === "guild" ? (_peer, subPeer, item) => ctx.super.messages.sendChannelMsg(subPeer, item) : (peer, _subPeer, item) => ctx.super.messages.sendDmsMsg(peer, item);
2224
2199
  for (const item of items) {
2225
2200
  passive(item);
2226
2201
  const res = await send(contact.peer, contact.subPeer, item);
@@ -2424,7 +2399,7 @@ const prepareStaticElement = async (ctx, element) => {
2424
2399
  */
2425
2400
  const selfSender = (ctx, contact) => {
2426
2401
  const userId = ctx.selfSubId("id") || ctx.selfId;
2427
- const name = ctx.selfName || ctx.cfg.name || "";
2402
+ const name = ctx.selfName || "";
2428
2403
  if (contact.scene === "friend" || contact.scene === "direct") return karin$1.friendSender(userId, name);
2429
2404
  return karin$1.groupSender(userId, "member", name);
2430
2405
  };
@@ -2756,8 +2731,13 @@ var AdapterQQBot = class extends AdapterBase {
2756
2731
  /**
2757
2732
  * 禁言群成员
2758
2733
  */
2759
- async setGroupMute(_groupId, _targetId, _duration) {
2760
- this.logger("warn", "[setGroupMute] QQ Official Bot API 暂不支持禁言群成员");
2734
+ async setGroupMute(_groupId, _targetId, duration) {
2735
+ let rfc3339 = "";
2736
+ if (duration > 0) {
2737
+ const time = Date.now() + duration * 1e3;
2738
+ rfc3339 = new Date(time).toISOString();
2739
+ }
2740
+ this.super.groups.setGroupMetu(_groupId, _targetId, rfc3339 ? "add" : "del", rfc3339);
2761
2741
  }
2762
2742
  /**
2763
2743
  * 群全员禁言
@@ -2840,7 +2820,7 @@ var AdapterQQBot = class extends AdapterBase {
2840
2820
  avatar: await this.getGroupAvatarUrl(_groupId)
2841
2821
  };
2842
2822
  } catch (err) {
2843
- this.logger("warn", `[getGroupInfo] 获取群信息失败(该接口为白名单接口,未开通权限会返回 11253): ${err instanceof Error ? err.message : err}`);
2823
+ this.logger("warn", `[getGroupInfo] 获取群信息失败: ${err instanceof Error ? err.message : err}`);
2844
2824
  return empty;
2845
2825
  }
2846
2826
  }
@@ -3783,6 +3763,50 @@ const onC2CMsgReject = (client, event) => {
3783
3763
  const userId = event.d.openid;
3784
3764
  client.logger("mark", `好友 ${userId} 已拒绝机器人主动发送消息`);
3785
3765
  };
3766
+ /** 用户申请加群事件 */
3767
+ const onGroupJoinRequest = (client, event) => {
3768
+ const eventId = event.id;
3769
+ const contact = karin$1.contactGroup(event.d.group_openid);
3770
+ const srcReply = (elements) => client.sendMsg(contact, [...elements, segment.pasmsg(eventId, "event")]);
3771
+ const time = Date.parse(event.d.apply_at);
3772
+ if (event.d.member_openid === client.selfId) createGroupInviteRequest({
3773
+ bot: client,
3774
+ time,
3775
+ eventId,
3776
+ contact,
3777
+ rawEvent: event,
3778
+ subEvent: "groupInvite",
3779
+ sender: karin$1.groupSender(event.d.invited_by, "unknown"),
3780
+ srcReply,
3781
+ content: {
3782
+ inviterId: event.d.invited_by,
3783
+ flag: event.d.join_request_id
3784
+ }
3785
+ });
3786
+ else createGroupApplyRequest({
3787
+ bot: client,
3788
+ time,
3789
+ eventId,
3790
+ contact,
3791
+ rawEvent: event,
3792
+ subEvent: "groupApply",
3793
+ sender: karin$1.groupSender(event.d.member_openid, "unknown"),
3794
+ srcReply,
3795
+ content: {
3796
+ inviterId: event.d.invited_by ?? "",
3797
+ flag: event.d.join_request_id,
3798
+ groupId: event.d.group_openid,
3799
+ applierId: event.d.member_openid,
3800
+ reason: (() => {
3801
+ if (event.d.verify_info) if (event.d.verify_info.method === "admin_review_qa") {
3802
+ const reason = event.d.verify_info.review_qa_list[0];
3803
+ if (reason) return `问题: ${reason.question}\n答案: ${reason.answer}`;
3804
+ } else return event.d.verify_info.verify_message;
3805
+ return "";
3806
+ })()
3807
+ }
3808
+ });
3809
+ };
3786
3810
 
3787
3811
  //#endregion
3788
3812
  //#region src/core/event/interaction.ts
@@ -4017,6 +4041,9 @@ const dispatch$1 = (client, ev) => {
4017
4041
  return;
4018
4042
  case "MESSAGE_AUDIT_PASS": return client.logger("debug", `消息审核通过: audit_id=${ev.d.audit_id}`);
4019
4043
  case "MESSAGE_AUDIT_REJECT": return client.logger("debug", `消息审核不通过: audit_id=${ev.d.audit_id}`);
4044
+ case "GROUP_JOIN_REQUEST":
4045
+ onGroupJoinRequest(client, ev);
4046
+ return;
4020
4047
  default: client.logger("debug", `[QQ Official Bot] 未处理事件: ${ev.t}`);
4021
4048
  }
4022
4049
  };
@@ -4419,34 +4446,17 @@ const QUICK_DISCONNECT_THRESHOLD_MS = 5e3;
4419
4446
  const QUICK_DISCONNECT_LIMIT = 3;
4420
4447
  const QUICK_DISCONNECT_DELAY_MS = 6e4;
4421
4448
  /**
4422
- * 从 proxy.prodApi / proxy.sandboxApi 推导兜底的 WS 地址
4423
- * 官方实际长期返回的也是 wss://{host}/websocket/,不依赖 /gateway 也能直连
4424
- */
4425
- const buildFallbackWsUrl = (cfg) => {
4426
- return buildFallbackWsUrlFromApi(cfg.sandbox ? cfg.proxy.sandboxApi : cfg.proxy.prodApi);
4427
- };
4428
- /**
4429
4449
  * 获取网关地址:优先调 /gateway;失败 / 超时 / 限频 → 直接 fallback 到硬编码
4430
4450
  */
4431
4451
  const fetchGateway = async (cfg) => {
4432
- const apiUrl = normalizeHttpUrl(cfg.sandbox ? cfg.proxy.sandboxApi : cfg.proxy.prodApi);
4433
- const fallback = buildFallbackWsUrl(cfg);
4434
- const proxyWsValue = cfg.sandbox ? cfg.proxy.sandboxWs : cfg.proxy.prodWs;
4435
- if (proxyWsValue.trim()) try {
4436
- const proxyWsUrl = normalizeWsGatewayUrl(proxyWsValue);
4437
- log("debug", `${cfg.appId}: 使用自定义 WS ${proxyWsUrl}`);
4438
- return proxyWsUrl;
4439
- } catch (err) {
4440
- const reason = err?.message || "unknown";
4441
- log("warn", `${cfg.appId}: 自定义 WS 地址无效 (${reason}),尝试获取 /gateway`);
4442
- }
4452
+ const apiUrl = "https://api.bot.qq.com";
4443
4453
  const accessToken = getBotAccessToken(cfg.appId);
4444
4454
  if (!accessToken) {
4445
- log("warn", `${cfg.appId}: 无 access_token,使用硬编码 WS ${fallback}`);
4446
- return fallback;
4455
+ log("warn", `${cfg.appId}: 无 access_token,使用硬编码 WS`);
4456
+ return "wss://api.bot.qq.com/websocket";
4447
4457
  }
4448
4458
  try {
4449
- const { data } = await axios.get(joinHttpPath(apiUrl, "gateway"), {
4459
+ const { data } = await axios.get(path$1.join(apiUrl, "gateway"), {
4450
4460
  headers: {
4451
4461
  Authorization: `QQBot ${accessToken}`,
4452
4462
  "User-Agent": getUserAgent()
@@ -4454,20 +4464,20 @@ const fetchGateway = async (cfg) => {
4454
4464
  timeout: GATEWAY_FETCH_TIMEOUT_MS
4455
4465
  });
4456
4466
  if (data?.url) return data.url;
4457
- log("warn", `${cfg.appId}: /gateway 返回为空,使用硬编码 WS ${fallback}`);
4458
- return fallback;
4467
+ log("warn", `${cfg.appId}: /gateway 返回为空,使用硬编码 WS`);
4468
+ return "wss://api.bot.qq.com/websocket";
4459
4469
  } catch (err) {
4460
4470
  if (axios.isAxiosError(err)) {
4461
4471
  const response = err.response;
4462
4472
  const status = response?.status ?? 0;
4463
4473
  const data = response?.data;
4464
4474
  const detail = formatOpenAPIError(status, typeof data?.err_code === "number" ? data.err_code : typeof data?.code === "number" ? data.code : void 0, typeof data?.message === "string" ? data.message : void 0);
4465
- log("warn", `${cfg.appId}: /gateway 调用失败 | ${detail},使用硬编码 WS ${fallback}`);
4475
+ log("warn", `${cfg.appId}: /gateway 调用失败 | ${detail},使用硬编码 WS`);
4466
4476
  } else {
4467
4477
  const reason = err?.message || "unknown";
4468
- log("warn", `${cfg.appId}: /gateway 调用失败 (${reason}),使用硬编码 WS ${fallback}`);
4478
+ log("warn", `${cfg.appId}: /gateway 调用失败 (${reason}),使用硬编码 WS`);
4469
4479
  }
4470
- return fallback;
4480
+ return "wss://api.bot.qq.com/websocket";
4471
4481
  }
4472
4482
  };
4473
4483
  /** 重连退避(毫秒) */
@@ -4597,7 +4607,7 @@ const onClose = (cfg, intents, attempt, event, client) => {
4597
4607
  if (code === 4004) {
4598
4608
  log("warn", `${cfg.appId}: access_token 已失效,刷新 token 后重连...`);
4599
4609
  schedule(cfg.appId, 1e3, () => {
4600
- getAccessToken(cfg.proxy.tokenApi, cfg.appId, cfg.secret).then(() => connect(cfg, intents, 0)).catch(() => {
4610
+ getAccessToken(cfg.appId, cfg.secret).then(() => connect(cfg, intents, 0)).catch(() => {
4601
4611
  log("error", `${cfg.appId}: access_token 刷新失败,${RATE_LIMIT_DELAY_MS}ms 后重试连接...`);
4602
4612
  schedule(cfg.appId, RATE_LIMIT_DELAY_MS, () => connect(cfg, intents, attempt + 1));
4603
4613
  });
@@ -4678,10 +4688,7 @@ const initState = /* @__PURE__ */ new Map();
4678
4688
  const hashConfig = (bot) => JSON.stringify({
4679
4689
  appId: bot.appId,
4680
4690
  secret: bot.secret,
4681
- proxy: bot.proxy,
4682
- sandbox: bot.sandbox,
4683
4691
  event: bot.event,
4684
- keyboard: bot.keyboard,
4685
4692
  markdown: bot.markdown,
4686
4693
  messageCache: bot.messageCache
4687
4694
  });
@@ -4689,10 +4696,7 @@ const hashConfig = (bot) => JSON.stringify({
4689
4696
  * 创建单个 Bot 实例
4690
4697
  */
4691
4698
  const createBot = async (input) => {
4692
- const bot = {
4693
- ...input,
4694
- proxy: normalizeProxyConfig(input.proxy)
4695
- };
4699
+ const bot = { ...input };
4696
4700
  const appId = String(bot.appId);
4697
4701
  if (!appId) {
4698
4702
  logger.warn("[QQ Official Bot] 配置缺少 appId,跳过");
@@ -4709,14 +4713,13 @@ const createBot = async (input) => {
4709
4713
  await existing.promise.catch(() => {});
4710
4714
  }
4711
4715
  const promise = (async () => {
4712
- unregisterBot("selfId", appId);
4716
+ if (karin.getBot(appId)) unregisterBot("selfId", appId);
4713
4717
  stop(appId);
4714
4718
  offAll(appId);
4715
4719
  stopTokenRefresh(appId);
4716
4720
  logger.info(`[QQ Official Bot][${appId}] 获取 access_token...`);
4717
- await getAccessToken(bot.proxy.tokenApi, appId, bot.secret);
4718
- const baseUrl = bot.sandbox ? bot.proxy.sandboxApi : bot.proxy.prodApi;
4719
- const api = new QQBotApi(createAxiosInstance(baseUrl, appId));
4721
+ await getAccessToken(appId, bot.secret);
4722
+ const api = new QQBotApi(createAxiosInstance(appId));
4720
4723
  const me = await api.meta.getMe();
4721
4724
  const client = new AdapterQQBot(bot, api);
4722
4725
  client.account.name = me.username;
@@ -4730,7 +4733,7 @@ const createBot = async (input) => {
4730
4733
  bus.on(appId, (ev) => {
4731
4734
  dispatch$1(client, ev);
4732
4735
  });
4733
- client.adapter.address = baseUrl;
4736
+ client.adapter.address = QQAPIBASEURL;
4734
4737
  client.adapter.secret = "*********";
4735
4738
  client.adapter.version = pkg().version;
4736
4739
  if (bot.event.type === 2) {
@@ -4898,23 +4901,7 @@ const createRouting = () => {
4898
4901
  //#region src/index.ts
4899
4902
  logger.info(`${logger.violet(`[插件:${pkg().version}]`)} ${logger.green(basename)} 初始化完成~`);
4900
4903
  createRouting();
4901
- const printOnboardBanner = () => {
4902
- console.log();
4903
- console.log(" ==========================================");
4904
- console.log(" 未检测到 QQBot 配置");
4905
- console.log(" 请使用任意可用 bot(如 OneBot / Console 适配器)");
4906
- console.log(" 对机器人发送指令: #QQ登录");
4907
- console.log(" 扫码授权完成后会自动写入配置并完成初始化");
4908
- console.log();
4909
- console.log(" ⚠ 注意:扫码授权会刷新该机器人的 secret,旧 secret 立即失效");
4910
- console.log(" ==========================================");
4911
- console.log();
4912
- };
4913
4904
  const bootstrap = async () => {
4914
- if (needQrOnboard()) {
4915
- printOnboardBanner();
4916
- return;
4917
- }
4918
4905
  try {
4919
4906
  await initQQBotAdapter();
4920
4907
  } catch (err) {
@@ -0,0 +1,27 @@
1
+ import { logger } from "node-karin";
2
+
3
+ //#region src/utils/logger.ts
4
+ const PREFIX = "[QQ Official Bot]";
5
+ const m = {
6
+ trace: (...a) => logger.trace(PREFIX, ...a),
7
+ debug: (...a) => logger.debug(PREFIX, ...a),
8
+ info: (...a) => logger.info(PREFIX, ...a),
9
+ warn: (...a) => logger.warn(PREFIX, ...a),
10
+ error: (...a) => logger.error(PREFIX, ...a),
11
+ fatal: (...a) => logger.fatal(PREFIX, ...a),
12
+ mark: (...a) => logger.mark(PREFIX, ...a)
13
+ };
14
+ /**
15
+ * QQBot 日志函数
16
+ * - log('info', '消息')
17
+ * - log.info('消息')
18
+ *
19
+ * 日志级别由 karin 框架的 log.level 控制
20
+ */
21
+ function log(level, ...args) {
22
+ m[level](...args);
23
+ }
24
+ Object.assign(log, m);
25
+
26
+ //#endregion
27
+ export { log as t };
@@ -1,4 +1,5 @@
1
- import { m as log, n as config, p as pluginDirName } from "./config-BUuU6FnQ.js";
1
+ import { c as pluginDirName, n as config } from "./config-D1_LqrcJ.js";
2
+ import { t as log } from "./logger-sza6kuy3.js";
2
3
  import { karinPathBase, karinPathTemp } from "node-karin";
3
4
  import { createWriteStream } from "node:fs";
4
5
  import axios from "node-karin/axios";
@@ -0,0 +1,32 @@
1
+ import "./config-D1_LqrcJ.js";
2
+ import "./logger-sza6kuy3.js";
3
+ import { common, logger } from "node-karin";
4
+ import qrcode from "qrcode";
5
+ import size from "image-size";
6
+ import lodash from "node-karin/lodash";
7
+ import "@karinjs/geturls";
8
+
9
+ //#region src/utils/common.ts
10
+ /** QQBot统一API地址 */
11
+ const QQAPIBASEURL = "https://api.bot.qq.com";
12
+ /** 随机数 */
13
+ const random = (min, max) => lodash.random(min, max);
14
+ /**
15
+ * 伪事件日志(红字)
16
+ */
17
+ const fakeEvent = (log) => {
18
+ logger.fatal(`${logger.red("[QQ Official Bot][虚假事件]")} ${log.replace(/\n/g, "\\n")}`);
19
+ };
20
+ /**
21
+ * 读取图片宽高
22
+ */
23
+ const getImageSize = async (url) => {
24
+ const { width = 100, height = 100 } = size(await common.buffer(url));
25
+ return {
26
+ width,
27
+ height
28
+ };
29
+ };
30
+
31
+ //#endregion
32
+ export { random as i, fakeEvent as n, getImageSize as r, QQAPIBASEURL as t };
@@ -1,16 +1,11 @@
1
1
  //#region src/web.config.d.ts
2
2
  type WebQQBotInput = {
3
- name: unknown;
4
3
  appId: unknown;
5
4
  secret: unknown;
6
- 'proxy:api': unknown;
7
- 'proxy:ws': unknown;
8
- sandbox: unknown;
9
5
  qqEnable: unknown;
10
6
  guildEnable: unknown;
11
7
  guildMode: unknown;
12
8
  regex: unknown;
13
- 'keyboard:enable': unknown;
14
9
  'markdown:enable': unknown;
15
10
  'messageCache:enable': unknown;
16
11
  'messageCache:self': unknown;