@sfmc-bds/qq-bridge 0.2.0-beta.8 → 0.2.0
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/README.md +69 -2
- package/deploy/install-nginx-webhook.sh +34 -0
- package/deploy/nginx-webhook-location.conf +10 -0
- package/deploy/qq-bridge.service +23 -0
- package/deploy/qq-config-sync.ps1 +106 -0
- package/deploy/qq-tunnel.ps1 +15 -0
- package/deploy/sync-qq-config.mjs +229 -0
- package/dist/commands/account-format.js +41 -0
- package/dist/commands/account-format.js.map +7 -0
- package/dist/commands/content-format.js +114 -0
- package/dist/commands/content-format.js.map +7 -0
- package/dist/commands/db-api.js +21 -6
- package/dist/commands/db-api.js.map +2 -2
- package/dist/commands/handlers.js +392 -524
- package/dist/commands/handlers.js.map +2 -2
- package/dist/commands/index.js +5 -5
- package/dist/commands/index.js.map +3 -3
- package/dist/commands/interaction-router.js +38 -133
- package/dist/commands/interaction-router.js.map +2 -2
- package/dist/commands/join-settings-ui.js +1 -24
- package/dist/commands/join-settings-ui.js.map +2 -2
- package/dist/commands/menu-format.js +36 -33
- package/dist/commands/menu-format.js.map +2 -2
- package/dist/commands/menu-panel-sync.js +39 -13
- package/dist/commands/menu-panel-sync.js.map +2 -2
- package/dist/commands/message-text.js +91 -0
- package/dist/commands/message-text.js.map +7 -0
- package/dist/commands/pending.js +23 -16
- package/dist/commands/pending.js.map +2 -2
- package/dist/commands/permissions.js +17 -0
- package/dist/commands/permissions.js.map +7 -0
- package/dist/commands/registry.js +1 -1
- package/dist/commands/registry.js.map +2 -2
- package/dist/commands/render.js +7 -16
- package/dist/commands/render.js.map +2 -2
- package/dist/commands/reply-llbot.js +18 -7
- package/dist/commands/reply-llbot.js.map +2 -2
- package/dist/commands/reply-official.js +55 -39
- package/dist/commands/reply-official.js.map +3 -3
- package/dist/commands/router.js +116 -23
- package/dist/commands/router.js.map +2 -2
- package/dist/config.js +35 -27
- package/dist/config.js.map +2 -2
- package/dist/console.js +3 -3
- package/dist/console.js.map +2 -2
- package/dist/index.js +30 -16
- package/dist/index.js.map +2 -2
- package/dist/llbot-ws-api.js +2 -2
- package/dist/llbot-ws-api.js.map +2 -2
- package/dist/official/events.js +32 -7
- package/dist/official/events.js.map +2 -2
- package/dist/official/gateway.js +10 -1
- package/dist/official/gateway.js.map +2 -2
- package/dist/official/webhook.js +165 -0
- package/dist/official/webhook.js.map +7 -0
- package/dist/onebot.js +0 -4
- package/dist/onebot.js.map +2 -2
- package/package.json +4 -4
- package/dist/commands/join-settings-ui.test.js +0 -43
- package/dist/commands/join-settings-ui.test.js.map +0 -7
- package/dist/commands/menu-panel-sync.test.js +0 -27
- package/dist/commands/menu-panel-sync.test.js.map +0 -7
- package/dist/commands/render.test.js +0 -44
- package/dist/commands/render.test.js.map +0 -7
- package/dist/commands/router.test.js +0 -92
- package/dist/commands/router.test.js.map +0 -7
- package/dist/llbot-ws-api.test.js +0 -32
- package/dist/llbot-ws-api.test.js.map +0 -7
- package/dist/official/events.test.js +0 -123
- package/dist/official/events.test.js.map +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/join-settings-ui.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * commands/join-settings-ui.ts \u2014 \u5165\u670D\u914D\u7F6E\u9762\u677F\u6587\u6848\u4E0E\u4E92\u52A8\u6309\u94AE\uFF08DRY\uFF09\n *\n * official
|
|
5
|
-
"mappings": "AAgBA,SAAS,WAAW,IAAqB;AACvC,SAAO,KAAK,WAAM;AACpB;AAGO,SAAS,uBAAuB,GAAqB,SAAS,IAAY;AAC/E,QAAM,KAAK,WAAW,EAAE,sBAAsB,KAAK;AACnD,QAAM,KAAK,WAAW,EAAE,qBAAqB,KAAK;AAClD,QAAM,KAAK,WAAW,EAAE,iCAAiC,IAAI;AAC7D,QAAM,OAAO,SAAS,GAAG,MAAM;AAAA,IAAO;AACtC,SACE,GAAG,IAAI;AAAA,+BACE,EAAE;AAAA,yBACH,EAAE;AAAA,uDACG,EAAE;AAEnB;AAEO,SAAS,2BAA2B,GAAqB,QAAyB;AACvF,QAAM,KAAK,WAAW,EAAE,sBAAsB,KAAK;AACnD,QAAM,KAAK,WAAW,EAAE,qBAAqB,KAAK;AAClD,QAAM,KAAK,WAAW,EAAE,iCAAiC,IAAI;AAC7D,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,GAAI,SAAS,CAAC,IAAI,MAAM,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA,4BAAa,EAAE;AAAA,IACf,sBAAY,EAAE;AAAA,IACd,kDAAe,EAAE;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAMO,SAAS,yBACd,GACA,
|
|
4
|
+
"sourcesContent": ["/**\n * commands/join-settings-ui.ts \u2014 \u5165\u670D\u914D\u7F6E\u9762\u677F\u6587\u6848\u4E0E\u4E92\u52A8\u6309\u94AE\uFF08DRY\uFF09\n *\n * official \u4E0E llbot \u5747\u4F7F\u7528\u6587\u5B57\u547D\u4EE4\uFF0C\u7ECF\u8FC7\u7EDF\u4E00\u8DEF\u7531\u4E0E\u786E\u8BA4\n * llbot\uFF1A\u7F16\u53F7\u4F1A\u8BDD command = \u300C\u914D\u7F6E \u767D\u540D\u5355/\u5BA1\u6279 \u5F00|\u5173\u300D\n */\n\nimport type { JoinSettingsResponse } from \"./db-api.js\";\nimport type { CommandButton, CommandResult, QqBackendKind } from \"./types.js\";\n\nexport type JoinSettingsView = {\n allowlist_enabled?: boolean | undefined;\n require_approval?: boolean | undefined;\n treat_group_admins_as_admins?: boolean | undefined;\n};\n\nfunction onOffLabel(on: boolean): string {\n return on ? \"\u5F00\" : \"\u5173\";\n}\n\n/** \u7EAF\u6587\u672C\u6458\u8981\uFF08llbot / \u964D\u7EA7\uFF09 */\nexport function formatJoinSettingsText(s: JoinSettingsView, prefix = \"\"): string {\n const al = onOffLabel(s.allowlist_enabled !== false);\n const ap = onOffLabel(s.require_approval !== false);\n const ga = onOffLabel(s.treat_group_admins_as_admins === true);\n const head = prefix ? `${prefix}\\n` : \"\";\n return (\n `${head}\u5165\u670D\u63D2\u4EF6\u914D\u7F6E\uFF08configs/qq_link.json\uFF09\\n` +\n `\u00B7 \u767D\u540D\u5355\uFF1A${al}\\n` +\n `\u00B7 \u5BA1\u6279\uFF1A${ap}\\n` +\n `\u00B7 \u7FA4\u7BA1\u89C6\u4F5C\u7BA1\u7406\u5458\uFF1A${ga}\uFF08\u53EA\u8BFB\uFF0C\u6539\u6587\u4EF6\uFF09`\n );\n}\n\nexport function formatJoinSettingsMarkdown(s: JoinSettingsView, prefix?: string): string {\n const al = onOffLabel(s.allowlist_enabled !== false);\n const ap = onOffLabel(s.require_approval !== false);\n const ga = onOffLabel(s.treat_group_admins_as_admins === true);\n const lines = [\n \"## \u5165\u670D\u914D\u7F6E\",\n ...(prefix ? [\"\", prefix] : []),\n \"\",\n `| \u9879 | \u72B6\u6001 |`,\n `| --- | --- |`,\n `| \u767D\u540D\u5355 | **${al}** |`,\n `| \u5BA1\u6279 | **${ap}** |`,\n `| \u7FA4\u7BA1\u89C6\u4F5C\u7BA1\u7406\u5458 | ${ga}\uFF08\u53EA\u8BFB\uFF09 |`,\n \"\",\n \"_\u70B9\u6309\u94AE\u5207\u6362\u767D\u540D\u5355/\u5BA1\u6279\uFF1B\u7FA4\u7BA1\u5F00\u5173\u8BF7\u6539\u914D\u7F6E\u6587\u4EF6_\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * \u751F\u6210\u5207\u6362\u6309\u94AE\uFF1A\u5C55\u793A\u300C\u70B9\u4E00\u4E0B\u53D8\u6210\u7684\u76EE\u6807\u72B6\u6001\u300D\u3002\n * \u4E24\u7AEF\u4F7F\u7528\u76F8\u540C\u7684\u6587\u672C\u547D\u4EE4\u3002\n */\nexport function buildJoinSettingsButtons(\n s: JoinSettingsView,\n _backend: QqBackendKind,\n _adminOpenids: string[] = []\n): CommandButton[] {\n const alOn = s.allowlist_enabled !== false;\n const apOn = s.require_approval !== false;\n // \u70B9\u4E00\u4E0B\u5207\u5230\u53CD\u72B6\u6001\n const alTarget = !alOn;\n const apTarget = !apOn;\n const alWord = alTarget ? \"\u5F00\" : \"\u5173\";\n const apWord = apTarget ? \"\u5F00\" : \"\u5173\";\n\n return [\n {\n id: \"cfg_al\",\n label: `\u767D\u540D\u5355 ${alWord}`,\n command: `\u914D\u7F6E \u767D\u540D\u5355 ${alWord}`,\n },\n {\n id: \"cfg_ap\",\n label: `\u5BA1\u6279 ${apWord}`,\n command: `\u914D\u7F6E \u5BA1\u6279 ${apWord}`,\n },\n ];\n}\n\nexport function buildJoinSettingsPanel(opts: {\n settings: JoinSettingsView;\n backend: QqBackendKind;\n adminOpenids?: string[];\n /** \u5982\u300C\u5DF2\u4FDD\u5B58\u300D */\n prefix?: string;\n}): CommandResult {\n const { settings, backend, adminOpenids = [], prefix } = opts;\n const text = formatJoinSettingsText(settings, prefix);\n return {\n text,\n markdown: formatJoinSettingsMarkdown(settings, prefix),\n buttons: buildJoinSettingsButtons(settings, backend, adminOpenids),\n };\n}\n\n/** \u89E3\u6790 cfg:allowlist|approval:on|off */\nexport function parseCfgInteractionData(\n data: string\n): { field: \"allowlist_enabled\" | \"require_approval\"; value: boolean } | null {\n const m = /^cfg:(allowlist|approval):(on|off|true|false|1|0)$/i.exec(data.trim());\n if (!m) return null;\n const field = m[1]!.toLowerCase() === \"allowlist\" ? \"allowlist_enabled\" : \"require_approval\";\n const raw = m[2]!.toLowerCase();\n const value = raw === \"on\" || raw === \"true\" || raw === \"1\";\n return { field, value };\n}\n\nexport function settingsFromResponse(data: JoinSettingsResponse): JoinSettingsView {\n return {\n allowlist_enabled: data.allowlist_enabled,\n require_approval: data.require_approval,\n treat_group_admins_as_admins: data.treat_group_admins_as_admins,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAgBA,SAAS,WAAW,IAAqB;AACvC,SAAO,KAAK,WAAM;AACpB;AAGO,SAAS,uBAAuB,GAAqB,SAAS,IAAY;AAC/E,QAAM,KAAK,WAAW,EAAE,sBAAsB,KAAK;AACnD,QAAM,KAAK,WAAW,EAAE,qBAAqB,KAAK;AAClD,QAAM,KAAK,WAAW,EAAE,iCAAiC,IAAI;AAC7D,QAAM,OAAO,SAAS,GAAG,MAAM;AAAA,IAAO;AACtC,SACE,GAAG,IAAI;AAAA,+BACE,EAAE;AAAA,yBACH,EAAE;AAAA,uDACG,EAAE;AAEnB;AAEO,SAAS,2BAA2B,GAAqB,QAAyB;AACvF,QAAM,KAAK,WAAW,EAAE,sBAAsB,KAAK;AACnD,QAAM,KAAK,WAAW,EAAE,qBAAqB,KAAK;AAClD,QAAM,KAAK,WAAW,EAAE,iCAAiC,IAAI;AAC7D,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,GAAI,SAAS,CAAC,IAAI,MAAM,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA,4BAAa,EAAE;AAAA,IACf,sBAAY,EAAE;AAAA,IACd,kDAAe,EAAE;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAMO,SAAS,yBACd,GACA,UACA,gBAA0B,CAAC,GACV;AACjB,QAAM,OAAO,EAAE,sBAAsB;AACrC,QAAM,OAAO,EAAE,qBAAqB;AAEpC,QAAM,WAAW,CAAC;AAClB,QAAM,WAAW,CAAC;AAClB,QAAM,SAAS,WAAW,WAAM;AAChC,QAAM,SAAS,WAAW,WAAM;AAEhC,SAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,sBAAO,MAAM;AAAA,MACpB,SAAS,mCAAU,MAAM;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,gBAAM,MAAM;AAAA,MACnB,SAAS,6BAAS,MAAM;AAAA,IAC1B;AAAA,EACF;AACF;AAEO,SAAS,uBAAuB,MAMrB;AAChB,QAAM,EAAE,UAAU,SAAS,eAAe,CAAC,GAAG,OAAO,IAAI;AACzD,QAAM,OAAO,uBAAuB,UAAU,MAAM;AACpD,SAAO;AAAA,IACL;AAAA,IACA,UAAU,2BAA2B,UAAU,MAAM;AAAA,IACrD,SAAS,yBAAyB,UAAU,SAAS,YAAY;AAAA,EACnE;AACF;AAGO,SAAS,wBACd,MAC4E;AAC5E,QAAM,IAAI,sDAAsD,KAAK,KAAK,KAAK,CAAC;AAChF,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,QAAQ,EAAE,CAAC,EAAG,YAAY,MAAM,cAAc,sBAAsB;AAC1E,QAAM,MAAM,EAAE,CAAC,EAAG,YAAY;AAC9B,QAAM,QAAQ,QAAQ,QAAQ,QAAQ,UAAU,QAAQ;AACxD,SAAO,EAAE,OAAO,MAAM;AACxB;AAEO,SAAS,qBAAqB,MAA8C;AACjF,SAAO;AAAA,IACL,mBAAmB,KAAK;AAAA,IACxB,kBAAkB,KAAK;AAAA,IACvB,8BAA8B,KAAK;AAAA,EACrC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,44 +1,47 @@
|
|
|
1
1
|
function pickDisplayLabel(cmd, maxChars = 8) {
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
const labels = {
|
|
3
|
+
server: "\u670D\u52A1\u5668\u4FE1\u606F",
|
|
4
|
+
account: "\u6211\u7684\u8D26\u53F7",
|
|
5
|
+
entry: "\u8FDB\u670D\u8BF4\u660E",
|
|
6
|
+
help: "\u4F7F\u7528\u5E2E\u52A9",
|
|
7
|
+
admin: "\u670D\u52A1\u5668\u7BA1\u7406",
|
|
8
|
+
online: "\u5728\u7EBF\u73A9\u5BB6",
|
|
9
|
+
ip: "\u8FDE\u63A5\u5730\u5740",
|
|
10
|
+
channel: "\u804A\u5929\u4E92\u901A",
|
|
11
|
+
ping: "\u8FDE\u63A5\u68C0\u67E5",
|
|
12
|
+
modules: "\u6A21\u5757\u5217\u8868",
|
|
13
|
+
packs: "\u4E16\u754C\u5305"
|
|
14
|
+
};
|
|
15
|
+
const label = labels[cmd.name];
|
|
16
|
+
if (label) return label.slice(0, maxChars);
|
|
17
|
+
const candidates = [cmd.name, ...cmd.aliases].map((name) => name.replace(/^[//]+/, "").trim()).filter(Boolean);
|
|
18
|
+
return (candidates.find((name) => /[\u4e00-\u9fff]/.test(name)) ?? candidates[0] ?? cmd.name).slice(0, maxChars);
|
|
6
19
|
}
|
|
7
|
-
function
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
20
|
+
function formatCard(title, lines) {
|
|
21
|
+
return {
|
|
22
|
+
text: [title, "", ...lines].join("\n"),
|
|
23
|
+
markdown: [`## ${title}`, "", ...lines].join("\n")
|
|
24
|
+
};
|
|
13
25
|
}
|
|
14
26
|
function formatCommandMenu(opts) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return `- **${label}**\uFF08\`/${c.name}\`\uFF09\uFF1A${c.description}`;
|
|
23
|
-
});
|
|
27
|
+
const buttons = opts.cmds.map((cmd) => ({
|
|
28
|
+
id: `${opts.idPrefix}_${cmd.name}`,
|
|
29
|
+
label: pickDisplayLabel(cmd),
|
|
30
|
+
description: cmd.description,
|
|
31
|
+
command: `/${cmd.name}`
|
|
32
|
+
}));
|
|
33
|
+
const card = formatCard(opts.title, [opts.subtitle]);
|
|
24
34
|
return {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
markdown: `## ${title}
|
|
32
|
-
|
|
33
|
-
${subtitle}
|
|
34
|
-
|
|
35
|
-
${mdLines.join("\n")}
|
|
36
|
-
|
|
37
|
-
${footerMd}`,
|
|
38
|
-
buttons: buttonsFor(cmds, idPrefix)
|
|
35
|
+
...card,
|
|
36
|
+
menu: opts.home ? "home" : "section",
|
|
37
|
+
markdown: [card.markdown, "", ...opts.footerMd ? ["", opts.footerMd] : []].join("\n"),
|
|
38
|
+
text: card.text + (opts.footerText ? `
|
|
39
|
+
${opts.footerText}` : ""),
|
|
40
|
+
buttons
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
43
|
export {
|
|
44
|
+
formatCard,
|
|
42
45
|
formatCommandMenu,
|
|
43
46
|
pickDisplayLabel
|
|
44
47
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/menu-format.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/** \u83DC\u5355\u4E0E\u4FE1\u606F\u5361\u7EDF\u4E00\u6392\u7248\uFF0C\u907F\u514D\u6B63\u6587\u548C\u64CD\u4F5C\u533A\u91CD\u590D\u5217\u51FA\u6307\u4EE4\u3002 */\nimport type { CommandButton, CommandResult, RegisteredCommand } from \"./types.js\";\n\nexport function pickDisplayLabel(cmd: RegisteredCommand, maxChars = 8): string {\n const labels: Record<string, string> = {\n server: \"\u670D\u52A1\u5668\u4FE1\u606F\",\n account: \"\u6211\u7684\u8D26\u53F7\",\n entry: \"\u8FDB\u670D\u8BF4\u660E\",\n help: \"\u4F7F\u7528\u5E2E\u52A9\",\n admin: \"\u670D\u52A1\u5668\u7BA1\u7406\",\n online: \"\u5728\u7EBF\u73A9\u5BB6\",\n ip: \"\u8FDE\u63A5\u5730\u5740\",\n channel: \"\u804A\u5929\u4E92\u901A\",\n ping: \"\u8FDE\u63A5\u68C0\u67E5\",\n modules: \"\u6A21\u5757\u5217\u8868\",\n packs: \"\u4E16\u754C\u5305\",\n };\n const label = labels[cmd.name];\n if (label) return label.slice(0, maxChars);\n const candidates = [cmd.name, ...cmd.aliases].map((name) => name.replace(/^[/\uFF0F]+/, \"\").trim()).filter(Boolean);\n return (candidates.find((name) => /[\\u4e00-\\u9fff]/.test(name)) ?? candidates[0] ?? cmd.name).slice(0, maxChars);\n}\n\nexport function formatCard(title: string, lines: string[]): CommandResult {\n return {\n text: [title, \"\", ...lines].join(\"\\n\"),\n markdown: [`## ${title}`, \"\", ...lines].join(\"\\n\"),\n };\n}\n\nexport function formatCommandMenu(opts: {\n title: string;\n subtitle: string;\n cmds: RegisteredCommand[];\n idPrefix: string;\n footerMd: string;\n footerText: string;\n home?: boolean;\n}): CommandResult {\n const buttons: CommandButton[] = opts.cmds.map((cmd) => ({\n id: `${opts.idPrefix}_${cmd.name}`,\n label: pickDisplayLabel(cmd),\n description: cmd.description,\n command: `/${cmd.name}`,\n }));\n const card = formatCard(opts.title, [opts.subtitle]);\n return {\n ...card,\n menu: opts.home ? \"home\" : \"section\",\n markdown: [card.markdown, \"\", ...(opts.footerMd ? [\"\", opts.footerMd] : [])].join(\"\\n\"),\n text: card.text + (opts.footerText ? `\\n${opts.footerText}` : \"\"),\n buttons,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAGO,SAAS,iBAAiB,KAAwB,WAAW,GAAW;AAC7E,QAAM,SAAiC;AAAA,IACrC,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,IAAI,IAAI;AAC7B,MAAI,MAAO,QAAO,MAAM,MAAM,GAAG,QAAQ;AACzC,QAAM,aAAa,CAAC,IAAI,MAAM,GAAG,IAAI,OAAO,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ,UAAU,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAC7G,UAAQ,WAAW,KAAK,CAAC,SAAS,kBAAkB,KAAK,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,IAAI,MAAM,MAAM,GAAG,QAAQ;AACjH;AAEO,SAAS,WAAW,OAAe,OAAgC;AACxE,SAAO;AAAA,IACL,MAAM,CAAC,OAAO,IAAI,GAAG,KAAK,EAAE,KAAK,IAAI;AAAA,IACrC,UAAU,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,KAAK,IAAI;AAAA,EACnD;AACF;AAEO,SAAS,kBAAkB,MAQhB;AAChB,QAAM,UAA2B,KAAK,KAAK,IAAI,CAAC,SAAS;AAAA,IACvD,IAAI,GAAG,KAAK,QAAQ,IAAI,IAAI,IAAI;AAAA,IAChC,OAAO,iBAAiB,GAAG;AAAA,IAC3B,aAAa,IAAI;AAAA,IACjB,SAAS,IAAI,IAAI,IAAI;AAAA,EACvB,EAAE;AACF,QAAM,OAAO,WAAW,KAAK,OAAO,CAAC,KAAK,QAAQ,CAAC;AACnD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,KAAK,OAAO,SAAS;AAAA,IAC3B,UAAU,CAAC,KAAK,UAAU,IAAI,GAAI,KAAK,WAAW,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAE,EAAE,KAAK,IAAI;AAAA,IACtF,MAAM,KAAK,QAAQ,KAAK,aAAa;AAAA,EAAK,KAAK,UAAU,KAAK;AAAA,IAC9D;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { patchJson, readJson } from "@sfmc-bds/sdk/node/config";
|
|
1
2
|
import {
|
|
2
3
|
createPanel,
|
|
4
|
+
getPanel,
|
|
3
5
|
putMenu,
|
|
4
6
|
updatePanel,
|
|
5
7
|
updatePanelTarget
|
|
6
8
|
} from "@sfmc-bds/sdk/node/qq-official";
|
|
7
|
-
import { patchJson } from "@sfmc-bds/sdk/node/config";
|
|
8
|
-
import { log } from "../log.js";
|
|
9
9
|
import { CFG_PATH } from "../config.js";
|
|
10
|
+
import { log } from "../log.js";
|
|
10
11
|
import { pickDisplayLabel } from "./menu-format.js";
|
|
11
12
|
const MAX_MENU_ITEMS = 10;
|
|
12
13
|
const MAX_PANEL_ITEMS = 20;
|
|
@@ -26,8 +27,7 @@ function buildMenuItems(registry) {
|
|
|
26
27
|
}
|
|
27
28
|
function buildPanelItems(registry) {
|
|
28
29
|
const user = registry.userMenu();
|
|
29
|
-
const
|
|
30
|
-
const merged = [...user, ...admin].slice(0, MAX_PANEL_ITEMS);
|
|
30
|
+
const merged = user.slice(0, MAX_PANEL_ITEMS);
|
|
31
31
|
return merged.map((c) => ({
|
|
32
32
|
type: "command",
|
|
33
33
|
// 填入输入框的内容:用 /name 便于 router 命中
|
|
@@ -47,6 +47,21 @@ function extractPanelId(json) {
|
|
|
47
47
|
}
|
|
48
48
|
return "";
|
|
49
49
|
}
|
|
50
|
+
function panelTargetInfo(raw) {
|
|
51
|
+
if (!raw || typeof raw !== "object") return null;
|
|
52
|
+
const root = raw;
|
|
53
|
+
const data = root["data"];
|
|
54
|
+
const panel = data && typeof data === "object" ? data : root;
|
|
55
|
+
const scope = panel["scope"];
|
|
56
|
+
const targetType = panel["target_type"];
|
|
57
|
+
if (typeof scope !== "string" || typeof targetType !== "string") return null;
|
|
58
|
+
const groups = panel["group_openids"];
|
|
59
|
+
return {
|
|
60
|
+
scope,
|
|
61
|
+
targetType,
|
|
62
|
+
groupOpenids: Array.isArray(groups) ? groups.filter((id) => typeof id === "string") : []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
50
65
|
async function syncMenuAndPanel(opts) {
|
|
51
66
|
const items = buildMenuItems(opts.registry);
|
|
52
67
|
const panelItems = buildPanelItems(opts.registry);
|
|
@@ -64,10 +79,12 @@ async function syncMenuAndPanel(opts) {
|
|
|
64
79
|
remark: "SFMC"
|
|
65
80
|
};
|
|
66
81
|
let panelId = opts.panelId.trim();
|
|
82
|
+
let updatedExisting = false;
|
|
67
83
|
if (panelId) {
|
|
68
84
|
const upd = await updatePanel(opts.creds, panelId, { panel: panelBody });
|
|
69
85
|
if (upd.ok) {
|
|
70
86
|
panelOk = true;
|
|
87
|
+
updatedExisting = true;
|
|
71
88
|
log.info(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u5DF2\u66F4\u65B0 id=${panelId}`);
|
|
72
89
|
} else {
|
|
73
90
|
log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u66F4\u65B0\u5931\u8D25\uFF0C\u5C06\u5C1D\u8BD5\u65B0\u5EFA: ${upd.error}`);
|
|
@@ -96,13 +113,21 @@ async function syncMenuAndPanel(opts) {
|
|
|
96
113
|
log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u521B\u5EFA\u5931\u8D25: ${created.error}`);
|
|
97
114
|
}
|
|
98
115
|
}
|
|
99
|
-
if (
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
if (updatedExisting && panelId && opts.groupOpenid) {
|
|
117
|
+
const detail = await getPanel(opts.creds, panelId);
|
|
118
|
+
if (!detail.ok) {
|
|
119
|
+
log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u8BE6\u60C5\u67E5\u8BE2\u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7\u5173\u8054\u7FA4: ${detail.error}`);
|
|
120
|
+
} else {
|
|
121
|
+
const target = panelTargetInfo(detail.json ?? safeParse(detail.body));
|
|
122
|
+
if (!target) {
|
|
123
|
+
log.warn("\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u8BE6\u60C5\u7F3A\u5C11 scope/target_type\uFF0C\u5DF2\u8DF3\u8FC7\u5173\u8054\u7FA4");
|
|
124
|
+
} else if (target.scope === "group" && target.targetType === "specific" && !target.groupOpenids.includes(opts.groupOpenid)) {
|
|
125
|
+
const linked = await updatePanelTarget(opts.creds, panelId, {
|
|
126
|
+
op: "add",
|
|
127
|
+
group_openids: [opts.groupOpenid]
|
|
128
|
+
});
|
|
129
|
+
if (!linked.ok) log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u5173\u8054\u7FA4\u5931\u8D25: ${linked.error}`);
|
|
130
|
+
}
|
|
106
131
|
}
|
|
107
132
|
}
|
|
108
133
|
return { menuOk, panelOk };
|
|
@@ -116,9 +141,10 @@ function safeParse(body) {
|
|
|
116
141
|
}
|
|
117
142
|
function persistPanelIdToConfig(panelId) {
|
|
118
143
|
try {
|
|
119
|
-
|
|
144
|
+
const current = readJson(CFG_PATH) ?? {};
|
|
145
|
+
patchJson(CFG_PATH, { official: { ...current.official, group_panel_id: panelId } });
|
|
120
146
|
} catch (e) {
|
|
121
|
-
log.warn(`\u5199\u56DE
|
|
147
|
+
log.warn(`\u5199\u56DE official.group_panel_id \u5931\u8D25: ${e.message}`);
|
|
122
148
|
}
|
|
123
149
|
}
|
|
124
150
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/menu-panel-sync.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * commands/menu-panel-sync.ts \u2014 CommandRegistry \u2192 \u5B98\u65B9\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F\n *\n * \u5B98\u65B9\u5951\u7EA6\uFF08autogen\uFF09\uFF1A\n * PUT /v2/menu body: { menu: { items } }\n * POST /v2/panels body: { scope, target_type?, group_openids?, panel: { items, remark? } }\n * PUT /v2/panels/{id} body: { panel: { items, remark? } }\n */\n\nimport {\n createPanel,\n putMenu,\n updatePanel,\n updatePanelTarget,\n type QqMenuItem,\n type QqOfficialCredentials,\n type QqPanelItem,\n} from \"@sfmc-bds/sdk/node/qq-official\";\nimport {
|
|
5
|
-
"mappings": "AASA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * commands/menu-panel-sync.ts \u2014 CommandRegistry \u2192 \u5B98\u65B9\u83DC\u5355 / \u7FA4\u6307\u4EE4\u9762\u677F\n *\n * \u5B98\u65B9\u5951\u7EA6\uFF08autogen\uFF09\uFF1A\n * PUT /v2/menu body: { menu: { items } }\n * POST /v2/panels body: { scope, target_type?, group_openids?, panel: { items, remark? } }\n * PUT /v2/panels/{id} body: { panel: { items, remark? } }\n */\n\nimport { patchJson, readJson } from \"@sfmc-bds/sdk/node/config\";\nimport {\n createPanel,\n getPanel,\n putMenu,\n updatePanel,\n updatePanelTarget,\n type QqMenuItem,\n type QqOfficialCredentials,\n type QqPanelItem,\n} from \"@sfmc-bds/sdk/node/qq-official\";\nimport { CFG_PATH } from \"../config.js\";\nimport { log } from \"../log.js\";\nimport { pickDisplayLabel } from \"./menu-format.js\";\nimport type { CommandRegistry } from \"./registry.js\";\nimport type { RegisteredCommand } from \"./types.js\";\n\nconst MAX_MENU_ITEMS = 10;\nconst MAX_PANEL_ITEMS = 20;\n\nexport type MenuPanelSyncOpts = {\n creds: QqOfficialCredentials;\n registry: CommandRegistry;\n groupOpenid: string;\n /** \u5DF2\u6709\u9762\u677F id\uFF1B\u7A7A\u5219 POST \u65B0\u5EFA */\n panelId: string;\n /** sync \u6210\u529F\u540E\u5199\u56DE panel id */\n persistPanelId?: (panelId: string) => void;\n};\n\n/** \u9009\u4E00\u4E2A\u9002\u5408\u5C55\u793A/\u586B\u5165\u8F93\u5165\u6846\u7684\u77ED\u89E6\u53D1\u8BCD\uFF08\u4F18\u5148\u4E2D\u6587\u522B\u540D\uFF09 */\nfunction pickTriggerLabel(cmd: RegisteredCommand, maxChars: number): string {\n return pickDisplayLabel(cmd, maxChars);\n}\n\n/** \u4ECE registry \u751F\u6210 C2C \u83DC\u5355\u9879\uFF08send_message\uFF09\uFF1B\u4EC5\u7528\u6237\u5411\u6307\u4EE4 */\nexport function buildMenuItems(registry: CommandRegistry): QqMenuItem[] {\n return registry\n .userMenu()\n .slice(0, MAX_MENU_ITEMS)\n .map((c) => {\n const label = pickTriggerLabel(c, 10);\n return {\n type: \"send_message\" as const,\n name: label,\n // \u70B9\u51FB\u540E\u586B\u5165\u8F93\u5165\u6846\uFF1B\u4E0E router \u89E6\u53D1\u8BCD\u5BF9\u9F50\n send_message: `/${c.name}`,\n };\n });\n}\n\n/**\n * \u4ECE registry \u751F\u6210\u7FA4\u6307\u4EE4\u9762\u677F\u9879\u3002\n * \u5B98\u65B9\uFF1Atype=command \u65F6\u70B9\u51FB\u540E\u628A name \u586B\u5165\u8F93\u5165\u6846\uFF08\u65E0\u72EC\u7ACB command \u5B57\u6BB5\uFF09\u3002\n * \u5168\u7FA4\u5171\u4EAB\u9762\u677F\u53EA\u5C55\u793A\u73A9\u5BB6\u5165\u53E3\uFF1B\u7BA1\u7406\u5165\u53E3\u5728\u4E2A\u4EBA\u83DC\u5355\u9274\u6743\u540E\u663E\u793A\u3002\n */\nexport function buildPanelItems(registry: CommandRegistry): QqPanelItem[] {\n const user = registry.userMenu();\n const merged = user.slice(0, MAX_PANEL_ITEMS);\n return merged.map((c) => ({\n type: \"command\" as const,\n // \u586B\u5165\u8F93\u5165\u6846\u7684\u5185\u5BB9\uFF1A\u7528 /name \u4FBF\u4E8E router \u547D\u4E2D\n name: `/${c.name}`.slice(0, 14),\n desc: (c.description || c.name).slice(0, 30),\n }));\n}\n\nfunction extractPanelId(json: unknown): string {\n if (!json || typeof json !== \"object\") return \"\";\n const o = json as Record<string, unknown>;\n for (const key of [\"panel_id\", \"id\", \"panelId\"]) {\n const v = o[key];\n if (typeof v === \"string\" && v.length > 0) return v;\n }\n if (o[\"data\"] && typeof o[\"data\"] === \"object\") {\n return extractPanelId(o[\"data\"]);\n }\n return \"\";\n}\n\nfunction panelTargetInfo(raw: unknown): { scope: string; targetType: string; groupOpenids: string[] } | null {\n if (!raw || typeof raw !== \"object\") return null;\n const root = raw as Record<string, unknown>;\n const data = root[\"data\"];\n const panel = data && typeof data === \"object\" ? (data as Record<string, unknown>) : root;\n const scope = panel[\"scope\"];\n const targetType = panel[\"target_type\"];\n if (typeof scope !== \"string\" || typeof targetType !== \"string\") return null;\n const groups = panel[\"group_openids\"];\n return {\n scope,\n targetType,\n groupOpenids: Array.isArray(groups) ? groups.filter((id): id is string => typeof id === \"string\") : [],\n };\n}\n\n/**\n * \u540C\u6B65\u81EA\u5B9A\u4E49\u83DC\u5355 + \u7FA4\u6307\u4EE4\u9762\u677F\u3002\u5931\u8D25\u53EA\u6253\u65E5\u5FD7\uFF0C\u4E0D\u629B\u7ED9\u8C03\u7528\u65B9\u3002\n */\nexport async function syncMenuAndPanel(opts: MenuPanelSyncOpts): Promise<{ menuOk: boolean; panelOk: boolean }> {\n const items = buildMenuItems(opts.registry);\n const panelItems = buildPanelItems(opts.registry);\n let menuOk = false;\n let panelOk = false;\n\n const menuRes = await putMenu(opts.creds, { menu: { items } });\n if (menuRes.ok) {\n menuOk = true;\n log.info(`\u5B98\u65B9\u81EA\u5B9A\u4E49\u83DC\u5355\u5DF2\u540C\u6B65 (${items.length} \u9879)`);\n } else {\n log.warn(`\u5B98\u65B9\u81EA\u5B9A\u4E49\u83DC\u5355\u540C\u6B65\u5931\u8D25: ${menuRes.error}`);\n }\n\n const panelBody = {\n items: panelItems,\n remark: \"SFMC\",\n };\n\n let panelId = opts.panelId.trim();\n let updatedExisting = false;\n if (panelId) {\n const upd = await updatePanel(opts.creds, panelId, { panel: panelBody });\n if (upd.ok) {\n panelOk = true;\n updatedExisting = true;\n log.info(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u5DF2\u66F4\u65B0 id=${panelId}`);\n } else {\n log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u66F4\u65B0\u5931\u8D25\uFF0C\u5C06\u5C1D\u8BD5\u65B0\u5EFA: ${upd.error}`);\n panelId = \"\";\n }\n }\n\n if (!panelId) {\n const hasGroup = !!opts.groupOpenid;\n const created = await createPanel(opts.creds, {\n scope: \"group\",\n target_type: hasGroup ? \"specific\" : \"all\",\n ...(hasGroup ? { group_openids: [opts.groupOpenid] } : {}),\n panel: panelBody,\n });\n if (created.ok) {\n panelId = extractPanelId(created.json) || extractPanelId(safeParse(created.body));\n if (panelId) {\n panelOk = true;\n opts.persistPanelId?.(panelId);\n log.info(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u5DF2\u521B\u5EFA id=${panelId}`);\n } else {\n log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u521B\u5EFA\u6210\u529F\u4F46\u672A\u89E3\u6790\u5230 id: ${created.body.slice(0, 160)}`);\n panelOk = true;\n }\n } else {\n log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u521B\u5EFA\u5931\u8D25: ${created.error}`);\n }\n }\n\n // \u65B0\u5EFA specific \u9762\u677F\u5DF2\u5E26\u7FA4\uFF1B\u5DF2\u6709\u9762\u677F\u9700\u5148\u786E\u8BA4\u7C7B\u578B\uFF0C\u4E0D\u80FD\u7ED9 all \u9762\u677F\u8FFD\u52A0\u5173\u8054\u5BF9\u8C61\u3002\n if (updatedExisting && panelId && opts.groupOpenid) {\n const detail = await getPanel(opts.creds, panelId);\n if (!detail.ok) {\n log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u8BE6\u60C5\u67E5\u8BE2\u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7\u5173\u8054\u7FA4: ${detail.error}`);\n } else {\n const target = panelTargetInfo(detail.json ?? safeParse(detail.body));\n if (!target) {\n log.warn(\"\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u8BE6\u60C5\u7F3A\u5C11 scope/target_type\uFF0C\u5DF2\u8DF3\u8FC7\u5173\u8054\u7FA4\");\n } else if (\n target.scope === \"group\" &&\n target.targetType === \"specific\" &&\n !target.groupOpenids.includes(opts.groupOpenid)\n ) {\n const linked = await updatePanelTarget(opts.creds, panelId, {\n op: \"add\",\n group_openids: [opts.groupOpenid],\n });\n if (!linked.ok) log.warn(`\u5B98\u65B9\u7FA4\u6307\u4EE4\u9762\u677F\u5173\u8054\u7FA4\u5931\u8D25: ${linked.error}`);\n }\n }\n }\n\n return { menuOk, panelOk };\n}\n\nfunction safeParse(body: string): unknown {\n try {\n return JSON.parse(body) as unknown;\n } catch {\n return null;\n }\n}\n\n/** \u628A panel id \u5199\u56DE official \u5206\u7EC4\uFF0C\u540C\u65F6\u4FDD\u7559\u5176\u4ED6\u5B98\u65B9\u8BBE\u7F6E\u3002 */\nexport function persistPanelIdToConfig(panelId: string): void {\n try {\n const current = readJson<{ official?: Record<string, unknown> }>(CFG_PATH) ?? {};\n patchJson(CFG_PATH, { official: { ...current.official, group_panel_id: panelId } });\n } catch (e) {\n log.warn(`\u5199\u56DE official.group_panel_id \u5931\u8D25: ${(e as Error).message}`);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
function officialPlainFallback(result) {
|
|
2
|
+
if (!result.menu || !result.buttons?.length) return result.text;
|
|
3
|
+
const items = result.buttons.filter((button) => button.command !== "/menu").map((button) => `${button.label}${button.description ? ` \xB7 ${button.description}` : ""}`);
|
|
4
|
+
const home = result.buttons.some((button) => button.command === "/menu") ? ["", "\u53D1\u9001\u300C\u83DC\u5355\u300D\u8FD4\u56DE\u9996\u9875\u3002"] : [];
|
|
5
|
+
return [result.text, "", ...items, ...home].join("\n");
|
|
6
|
+
}
|
|
7
|
+
function isTableRow(line) {
|
|
8
|
+
return line.trim().startsWith("|");
|
|
9
|
+
}
|
|
10
|
+
function isTableSeparator(line) {
|
|
11
|
+
const trimmed = line.trim();
|
|
12
|
+
if (!trimmed.includes("|")) return false;
|
|
13
|
+
const cells = trimmed.replace(/^\|/, "").replace(/\|$/, "").split("|");
|
|
14
|
+
return cells.length > 0 && cells.every((cell) => /^\s*:?-{3,}:?\s*$/.test(cell));
|
|
15
|
+
}
|
|
16
|
+
function byteLength(lines) {
|
|
17
|
+
return Buffer.byteLength(lines.join("\n"), "utf8");
|
|
18
|
+
}
|
|
19
|
+
function splitMarkdownMessage(markdown, maxBytes = 3e3) {
|
|
20
|
+
const lines = markdown.replace(/\r\n/g, "\n").split("\n");
|
|
21
|
+
const parts = [];
|
|
22
|
+
let bucket = [];
|
|
23
|
+
let tableHeader = [];
|
|
24
|
+
let sectionTitle = "";
|
|
25
|
+
const flush = () => {
|
|
26
|
+
const text = bucket.join("\n").trim();
|
|
27
|
+
if (text) parts.push(text);
|
|
28
|
+
bucket = [];
|
|
29
|
+
};
|
|
30
|
+
const seedContinuation = () => {
|
|
31
|
+
const seed = [];
|
|
32
|
+
if (sectionTitle) seed.push(`${sectionTitle}\uFF08\u7EED\uFF09`, "");
|
|
33
|
+
if (tableHeader.length) seed.push(...tableHeader);
|
|
34
|
+
bucket = seed;
|
|
35
|
+
};
|
|
36
|
+
for (let index = 0; index < lines.length; index++) {
|
|
37
|
+
const line = lines[index] ?? "";
|
|
38
|
+
const next = lines[index + 1];
|
|
39
|
+
const headerPair = isTableRow(line) && next !== void 0 && isTableSeparator(next) ? [line, next] : null;
|
|
40
|
+
const incoming = headerPair ?? [line];
|
|
41
|
+
if (bucket.length > 0 && byteLength([...bucket, ...incoming]) > maxBytes) {
|
|
42
|
+
flush();
|
|
43
|
+
if (!headerPair && isTableRow(line) && !isTableSeparator(line) && tableHeader.length) seedContinuation();
|
|
44
|
+
}
|
|
45
|
+
if (headerPair) {
|
|
46
|
+
tableHeader = headerPair;
|
|
47
|
+
bucket.push(...headerPair);
|
|
48
|
+
index += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (/^###\s+/.test(line)) {
|
|
52
|
+
sectionTitle = line;
|
|
53
|
+
tableHeader = [];
|
|
54
|
+
} else if (/^##\s+/.test(line)) {
|
|
55
|
+
sectionTitle = "";
|
|
56
|
+
tableHeader = [];
|
|
57
|
+
}
|
|
58
|
+
bucket.push(line);
|
|
59
|
+
}
|
|
60
|
+
flush();
|
|
61
|
+
return parts.length ? parts : [""];
|
|
62
|
+
}
|
|
63
|
+
function splitMessage(text, maxBytes = 3e3) {
|
|
64
|
+
const parts = [];
|
|
65
|
+
let current = "";
|
|
66
|
+
for (const line of text.split(/(?<=\n)/u)) {
|
|
67
|
+
if (Buffer.byteLength(current + line, "utf8") <= maxBytes) {
|
|
68
|
+
current += line;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (current) {
|
|
72
|
+
parts.push(current);
|
|
73
|
+
current = "";
|
|
74
|
+
}
|
|
75
|
+
for (const char of line) {
|
|
76
|
+
if (Buffer.byteLength(current + char, "utf8") > maxBytes) {
|
|
77
|
+
parts.push(current);
|
|
78
|
+
current = "";
|
|
79
|
+
}
|
|
80
|
+
current += char;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (current) parts.push(current);
|
|
84
|
+
return parts.length ? parts : [""];
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
officialPlainFallback,
|
|
88
|
+
splitMarkdownMessage,
|
|
89
|
+
splitMessage
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=message-text.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/message-text.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CommandResult } from \"./types.js\";\n\n/** \u5B98\u65B9 Markdown \u53D1\u9001\u5931\u8D25\u65F6\uFF0C\u83DC\u5355\u4EE5\u7B80\u6D01\u6587\u672C\u5448\u73B0\uFF1B\u6309\u94AE\u547D\u4EE4\u4E0D\u91CD\u590D\u5199\u5165\u6B63\u6587\u3002 */\nexport function officialPlainFallback(result: CommandResult): string {\n if (!result.menu || !result.buttons?.length) return result.text;\n const items = result.buttons\n .filter((button) => button.command !== \"/menu\")\n .map((button) => `${button.label}${button.description ? ` \u00B7 ${button.description}` : \"\"}`);\n const home = result.buttons.some((button) => button.command === \"/menu\")\n ? [\"\", \"\u53D1\u9001\u300C\u83DC\u5355\u300D\u8FD4\u56DE\u9996\u9875\u3002\"]\n : [];\n return [result.text, \"\", ...items, ...home].join(\"\\n\");\n}\n\nfunction isTableRow(line: string): boolean {\n return line.trim().startsWith(\"|\");\n}\n\n/** \u8868\u683C\u5206\u9694\u884C\uFF0C\u4F8B\u5982 `| --- | --- |`\u3002\u666E\u901A `---` \u5206\u5272\u7EBF\u4E0D\u7B97\u3002 */\nfunction isTableSeparator(line: string): boolean {\n const trimmed = line.trim();\n if (!trimmed.includes(\"|\")) return false;\n const cells = trimmed.replace(/^\\|/, \"\").replace(/\\|$/, \"\").split(\"|\");\n return cells.length > 0 && cells.every((cell) => /^\\s*:?-{3,}:?\\s*$/.test(cell));\n}\n\nfunction byteLength(lines: string[]): number {\n return Buffer.byteLength(lines.join(\"\\n\"), \"utf8\");\n}\n\n/**\n * \u6309\u884C\u62C6\u5206 Markdown\uFF0C\u5E76\u4FDD\u8BC1\u6BCF\u4E00\u6BB5\u91CC\u7684\u8868\u683C\u4ECD\u7136\u5B8C\u6574\u3002\n * \u4E00\u5F20\u8868\u8D85\u957F\u65F6\uFF0C\u4E0B\u4E00\u6BB5\u91CD\u590D\u8868\u5934\uFF0C\u5E76\u5728\u5C0F\u8282\u6807\u9898\u540E\u6807\u660E\u300C\u7EED\u300D\u3002\n * \u5F53\u524D\u7ED9 QQ \u4E16\u754C\u5305\u8868\u683C\u4F7F\u7528\uFF1A\u5B98\u65B9\u6D88\u606F\u6309\u6BB5\u53D1\u9001\uFF0C\u534A\u5F20\u8868\u65E0\u6CD5\u6E32\u67D3\u3002\n */\nexport function splitMarkdownMessage(markdown: string, maxBytes = 3000): string[] {\n const lines = markdown.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\n const parts: string[] = [];\n let bucket: string[] = [];\n let tableHeader: string[] = [];\n let sectionTitle = \"\";\n\n const flush = (): void => {\n const text = bucket.join(\"\\n\").trim();\n if (text) parts.push(text);\n bucket = [];\n };\n\n const seedContinuation = (): void => {\n const seed: string[] = [];\n if (sectionTitle) seed.push(`${sectionTitle}\uFF08\u7EED\uFF09`, \"\");\n if (tableHeader.length) seed.push(...tableHeader);\n bucket = seed;\n };\n\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? \"\";\n const next = lines[index + 1];\n const headerPair = isTableRow(line) && next !== undefined && isTableSeparator(next) ? [line, next] : null;\n const incoming = headerPair ?? [line];\n if (bucket.length > 0 && byteLength([...bucket, ...incoming]) > maxBytes) {\n flush();\n if (!headerPair && isTableRow(line) && !isTableSeparator(line) && tableHeader.length) seedContinuation();\n }\n if (headerPair) {\n tableHeader = headerPair;\n bucket.push(...headerPair);\n index += 1;\n continue;\n }\n if (/^###\\s+/.test(line)) {\n sectionTitle = line;\n tableHeader = [];\n } else if (/^##\\s+/.test(line)) {\n sectionTitle = \"\";\n tableHeader = [];\n }\n bucket.push(line);\n }\n flush();\n return parts.length ? parts : [\"\"];\n}\n\n/** \u4FDD\u5B88\u7684 UTF-8 \u5B57\u8282\u9884\u7B97\uFF1B\u4F18\u5148\u6309\u884C\u62C6\u5206\uFF0C\u8D85\u957F\u884C\u6309\u7801\u70B9\u62C6\u5206\u3002 */\nexport function splitMessage(text: string, maxBytes = 3000): string[] {\n const parts: string[] = [];\n let current = \"\";\n for (const line of text.split(/(?<=\\n)/u)) {\n if (Buffer.byteLength(current + line, \"utf8\") <= maxBytes) {\n current += line;\n continue;\n }\n if (current) {\n parts.push(current);\n current = \"\";\n }\n for (const char of line) {\n if (Buffer.byteLength(current + char, \"utf8\") > maxBytes) {\n parts.push(current);\n current = \"\";\n }\n current += char;\n }\n }\n if (current) parts.push(current);\n return parts.length ? parts : [\"\"];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/commands/pending.js
CHANGED
|
@@ -1,27 +1,34 @@
|
|
|
1
|
-
const DEFAULT_TTL_MS = 6e4;
|
|
2
1
|
class PendingChoiceStore {
|
|
3
|
-
|
|
4
|
-
ttlMs;
|
|
5
|
-
constructor(ttlMs = DEFAULT_TTL_MS) {
|
|
2
|
+
constructor(ttlMs = 6e4) {
|
|
6
3
|
this.ttlMs = ttlMs;
|
|
7
4
|
}
|
|
5
|
+
ttlMs;
|
|
6
|
+
map = /* @__PURE__ */ new Map();
|
|
8
7
|
key(backend, groupId, userId) {
|
|
9
|
-
return
|
|
8
|
+
return JSON.stringify([backend, groupId, userId]);
|
|
10
9
|
}
|
|
11
10
|
set(backend, groupId, userId, choices) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
11
|
+
for (const [key, entry] of this.map) {
|
|
12
|
+
if (entry.expiresAt + this.ttlMs < Date.now()) this.map.delete(key);
|
|
13
|
+
}
|
|
14
|
+
this.map.set(this.key(backend, groupId, userId), { choices, expiresAt: Date.now() + this.ttlMs });
|
|
15
|
+
}
|
|
16
|
+
choose(backend, groupId, userId, n) {
|
|
17
|
+
const key = this.key(backend, groupId, userId);
|
|
18
|
+
const entry = this.map.get(key);
|
|
19
|
+
if (!entry) return { kind: "missing" };
|
|
20
|
+
if (Date.now() >= entry.expiresAt) {
|
|
21
|
+
this.map.delete(key);
|
|
22
|
+
return { kind: "expired" };
|
|
23
|
+
}
|
|
24
|
+
const command = entry.choices.get(n);
|
|
25
|
+
if (!command) return { kind: "invalid" };
|
|
26
|
+
this.map.delete(key);
|
|
27
|
+
return { kind: "chosen", command };
|
|
16
28
|
}
|
|
17
|
-
/** 取编号对应命令;命中或过期都会清掉该会话 */
|
|
18
29
|
take(backend, groupId, userId, n) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
if (!entry) return null;
|
|
22
|
-
this.map.delete(k);
|
|
23
|
-
if (Date.now() > entry.expiresAt) return null;
|
|
24
|
-
return entry.choices.get(n) ?? null;
|
|
30
|
+
const result = this.choose(backend, groupId, userId, n);
|
|
31
|
+
return result.kind === "chosen" ? result.command : null;
|
|
25
32
|
}
|
|
26
33
|
clear(backend, groupId, userId) {
|
|
27
34
|
this.map.delete(this.key(backend, groupId, userId));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/pending.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/** \u7F16\u53F7\u83DC\u5355\u6309\u540E\u7AEF\u3001\u7FA4\u548C\u7528\u6237\u9694\u79BB\uFF1B\u9519\u8BEF\u8F93\u5165\u4E0D\u6D88\u8D39\u83DC\u5355\u3002 */\nexport type PendingChoice = { choices: Map<number, string>; expiresAt: number };\nexport type ChoiceResult = { kind: \"chosen\"; command: string } | { kind: \"missing\" | \"expired\" | \"invalid\" };\nexport class PendingChoiceStore {\n private readonly map = new Map<string, PendingChoice>();\n constructor(private readonly ttlMs = 60_000) {}\n private key(backend: string, groupId: string, userId: string): string {\n return JSON.stringify([backend, groupId, userId]);\n }\n set(backend: string, groupId: string, userId: string, choices: Map<number, string>): void {\n for (const [key, entry] of this.map) {\n if (entry.expiresAt + this.ttlMs < Date.now()) this.map.delete(key);\n }\n this.map.set(this.key(backend, groupId, userId), { choices, expiresAt: Date.now() + this.ttlMs });\n }\n choose(backend: string, groupId: string, userId: string, n: number): ChoiceResult {\n const key = this.key(backend, groupId, userId);\n const entry = this.map.get(key);\n if (!entry) return { kind: \"missing\" };\n if (Date.now() >= entry.expiresAt) {\n this.map.delete(key);\n return { kind: \"expired\" };\n }\n const command = entry.choices.get(n);\n if (!command) return { kind: \"invalid\" };\n this.map.delete(key);\n return { kind: \"chosen\", command };\n }\n take(backend: string, groupId: string, userId: string, n: number): string | null {\n const result = this.choose(backend, groupId, userId, n);\n return result.kind === \"chosen\" ? result.command : null;\n }\n clear(backend: string, groupId: string, userId: string): void {\n this.map.delete(this.key(backend, groupId, userId));\n }\n}\n"],
|
|
5
|
+
"mappings": "AAGO,MAAM,mBAAmB;AAAA,EAE9B,YAA6B,QAAQ,KAAQ;AAAhB;AAAA,EAAiB;AAAA,EAAjB;AAAA,EADZ,MAAM,oBAAI,IAA2B;AAAA,EAE9C,IAAI,SAAiB,SAAiB,QAAwB;AACpE,WAAO,KAAK,UAAU,CAAC,SAAS,SAAS,MAAM,CAAC;AAAA,EAClD;AAAA,EACA,IAAI,SAAiB,SAAiB,QAAgB,SAAoC;AACxF,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK;AACnC,UAAI,MAAM,YAAY,KAAK,QAAQ,KAAK,IAAI,EAAG,MAAK,IAAI,OAAO,GAAG;AAAA,IACpE;AACA,SAAK,IAAI,IAAI,KAAK,IAAI,SAAS,SAAS,MAAM,GAAG,EAAE,SAAS,WAAW,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC;AAAA,EAClG;AAAA,EACA,OAAO,SAAiB,SAAiB,QAAgB,GAAyB;AAChF,UAAM,MAAM,KAAK,IAAI,SAAS,SAAS,MAAM;AAC7C,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAC9B,QAAI,CAAC,MAAO,QAAO,EAAE,MAAM,UAAU;AACrC,QAAI,KAAK,IAAI,KAAK,MAAM,WAAW;AACjC,WAAK,IAAI,OAAO,GAAG;AACnB,aAAO,EAAE,MAAM,UAAU;AAAA,IAC3B;AACA,UAAM,UAAU,MAAM,QAAQ,IAAI,CAAC;AACnC,QAAI,CAAC,QAAS,QAAO,EAAE,MAAM,UAAU;AACvC,SAAK,IAAI,OAAO,GAAG;AACnB,WAAO,EAAE,MAAM,UAAU,QAAQ;AAAA,EACnC;AAAA,EACA,KAAK,SAAiB,SAAiB,QAAgB,GAA0B;AAC/E,UAAM,SAAS,KAAK,OAAO,SAAS,SAAS,QAAQ,CAAC;AACtD,WAAO,OAAO,SAAS,WAAW,OAAO,UAAU;AAAA,EACrD;AAAA,EACA,MAAM,SAAiB,SAAiB,QAAsB;AAC5D,SAAK,IAAI,OAAO,KAAK,IAAI,SAAS,SAAS,MAAM,CAAC;AAAA,EACpD;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { log } from "../log.js";
|
|
2
|
+
import { fetchJoinPending } from "./db-api.js";
|
|
3
|
+
async function authorizeAdmin(ctx) {
|
|
4
|
+
const { dbHost: host, dbPort: port } = ctx.runtimeInfo;
|
|
5
|
+
if (!host || !port || !ctx.inbound.userId) return false;
|
|
6
|
+
try {
|
|
7
|
+
const result = await fetchJoinPending({ host, port }, ctx.inbound.userId, ctx.inbound.isGroupAdmin === true);
|
|
8
|
+
return result.success === true && !result.error;
|
|
9
|
+
} catch (error) {
|
|
10
|
+
log.warn(`\u7BA1\u7406\u6743\u9650\u67E5\u8BE2\u5931\u8D25: ${String(error)}`);
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
authorizeAdmin
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/permissions.ts"],
|
|
4
|
+
"sourcesContent": ["import { log } from \"../log.js\";\nimport { fetchJoinPending } from \"./db-api.js\";\nimport type { CommandContext } from \"./types.js\";\n\n/** \u590D\u7528\u670D\u52A1\u7AEF\u6700\u7EC8\u9274\u6743\uFF0C\u6743\u9650\u4E0D\u53EF\u8FBE\u65F6\u5173\u95ED\u7BA1\u7406\u5165\u53E3\u3002 */\nexport async function authorizeAdmin(ctx: CommandContext): Promise<boolean> {\n const { dbHost: host, dbPort: port } = ctx.runtimeInfo;\n if (!host || !port || !ctx.inbound.userId) return false;\n try {\n const result = await fetchJoinPending({ host, port }, ctx.inbound.userId, ctx.inbound.isGroupAdmin === true);\n return result.success === true && !result.error;\n } catch (error) {\n log.warn(`\u7BA1\u7406\u6743\u9650\u67E5\u8BE2\u5931\u8D25: ${String(error)}`);\n return false;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW;AACpB,SAAS,wBAAwB;AAIjC,eAAsB,eAAe,KAAuC;AAC1E,QAAM,EAAE,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAI;AAC3C,MAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,OAAQ,QAAO;AAClD,MAAI;AACF,UAAM,SAAS,MAAM,iBAAiB,EAAE,MAAM,KAAK,GAAG,IAAI,QAAQ,QAAQ,IAAI,QAAQ,iBAAiB,IAAI;AAC3G,WAAO,OAAO,YAAY,QAAQ,CAAC,OAAO;AAAA,EAC5C,SAAS,OAAO;AACd,QAAI,KAAK,qDAAa,OAAO,KAAK,CAAC,EAAE;AACrC,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/registry.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * commands/registry.ts \u2014 \u6307\u4EE4\u6CE8\u518C\u4E0E\u67E5\u627E\n */\n\nimport type { RegisteredCommand } from \"./types.js\";\n\n/** \u53BB\u6389\u524D\u5BFC /\u3001\u7A7A\u767D\uFF0C\u7EDF\u4E00\u5C0F\u5199 */\nexport function normalizeTrigger(raw: string): string {\n return String(raw ?? \"\")\n .trim()\n .replace(/^[/\uFF0F]+/, \"\")\n .toLowerCase();\n}\n\nexport class CommandRegistry {\n private readonly byAlias = new Map<string, RegisteredCommand>();\n private readonly list: RegisteredCommand[] = [];\n\n register(cmd: RegisteredCommand): void {\n this.list.push(cmd);\n const names = new Set([cmd.name, ...cmd.aliases].map((a) => normalizeTrigger(a)));\n for (const alias of names) {\n if (!alias) continue;\n this.byAlias.set(alias, cmd);\n }\n }\n\n resolve(raw: string): RegisteredCommand | null {\n const key = normalizeTrigger(raw);\n if (!key) return null;\n const exact = this.byAlias.get(key);\n if (exact) return exact;\n // \u5E26\u53C2\u6570\uFF1A\u53D6\u9996\u8BCD\u5339\u914D\uFF08\u5982\u300C\u7533\u8BF7\u5165\u670D Steve\u300D\u2192 join\uFF09\n const first = key.split(/\\s+/)[0] ?? \"\";\n if (!first || first === key) return null;\n return this.byAlias.get(first) ?? null;\n }\n\n all(): readonly RegisteredCommand[] {\n return this.list;\n }\n\n /** \u4E3B\u83DC\u5355 / \u5B98\u65B9\u5FEB\u6377\u83DC\u5355\uFF1A\u6392\u9664\u7BA1\u7406\u5B50\u83DC\u5355\u9879 */\n userMenu(): RegisteredCommand[] {\n return this.list.filter((c) =>
|
|
5
|
-
"mappings": "AAOO,SAAS,iBAAiB,KAAqB;AACpD,SAAO,OAAO,OAAO,EAAE,EACpB,KAAK,EACL,QAAQ,UAAU,EAAE,EACpB,YAAY;AACjB;AAEO,MAAM,gBAAgB;AAAA,EACV,UAAU,oBAAI,IAA+B;AAAA,EAC7C,OAA4B,CAAC;AAAA,EAE9C,SAAS,KAA8B;AACrC,SAAK,KAAK,KAAK,GAAG;AAClB,UAAM,QAAQ,IAAI,IAAI,CAAC,IAAI,MAAM,GAAG,IAAI,OAAO,EAAE,IAAI,CAAC,MAAM,iBAAiB,CAAC,CAAC,CAAC;AAChF,eAAW,SAAS,OAAO;AACzB,UAAI,CAAC,MAAO;AACZ,WAAK,QAAQ,IAAI,OAAO,GAAG;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,QAAQ,KAAuC;AAC7C,UAAM,MAAM,iBAAiB,GAAG;AAChC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;AAClC,QAAI,MAAO,QAAO;AAElB,UAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,CAAC,KAAK;AACrC,QAAI,CAAC,SAAS,UAAU,IAAK,QAAO;AACpC,WAAO,KAAK,QAAQ,IAAI,KAAK,KAAK;AAAA,EACpC;AAAA,EAEA,MAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAgC;AAC9B,WAAO,KAAK,KAAK,OAAO,CAAC,MAAM,
|
|
4
|
+
"sourcesContent": ["/**\n * commands/registry.ts \u2014 \u6307\u4EE4\u6CE8\u518C\u4E0E\u67E5\u627E\n */\n\nimport type { RegisteredCommand } from \"./types.js\";\n\n/** \u53BB\u6389\u524D\u5BFC /\u3001\u7A7A\u767D\uFF0C\u7EDF\u4E00\u5C0F\u5199 */\nexport function normalizeTrigger(raw: string): string {\n return String(raw ?? \"\")\n .trim()\n .replace(/^[/\uFF0F]+/, \"\")\n .toLowerCase();\n}\n\nexport class CommandRegistry {\n private readonly byAlias = new Map<string, RegisteredCommand>();\n private readonly list: RegisteredCommand[] = [];\n\n register(cmd: RegisteredCommand): void {\n this.list.push(cmd);\n const names = new Set([cmd.name, ...cmd.aliases].map((a) => normalizeTrigger(a)));\n for (const alias of names) {\n if (!alias) continue;\n this.byAlias.set(alias, cmd);\n }\n }\n\n resolve(raw: string): RegisteredCommand | null {\n const key = normalizeTrigger(raw);\n if (!key) return null;\n const exact = this.byAlias.get(key);\n if (exact) return exact;\n // \u5E26\u53C2\u6570\uFF1A\u53D6\u9996\u8BCD\u5339\u914D\uFF08\u5982\u300C\u7533\u8BF7\u5165\u670D Steve\u300D\u2192 join\uFF09\n const first = key.split(/\\s+/)[0] ?? \"\";\n if (!first || first === key) return null;\n return this.byAlias.get(first) ?? null;\n }\n\n all(): readonly RegisteredCommand[] {\n return this.list;\n }\n\n /** \u4E3B\u83DC\u5355 / \u5B98\u65B9\u5FEB\u6377\u83DC\u5355\uFF1A\u6392\u9664\u7BA1\u7406\u5B50\u83DC\u5355\u9879 */\n userMenu(): RegisteredCommand[] {\n return this.list.filter((c) => c.group === \"home\" && c.permission !== \"admin\");\n }\n\n /** \u7BA1\u7406\u5B50\u83DC\u5355 */\n adminMenu(): RegisteredCommand[] {\n return this.list.filter((c) => !!c.adminMenu);\n }\n}\n"],
|
|
5
|
+
"mappings": "AAOO,SAAS,iBAAiB,KAAqB;AACpD,SAAO,OAAO,OAAO,EAAE,EACpB,KAAK,EACL,QAAQ,UAAU,EAAE,EACpB,YAAY;AACjB;AAEO,MAAM,gBAAgB;AAAA,EACV,UAAU,oBAAI,IAA+B;AAAA,EAC7C,OAA4B,CAAC;AAAA,EAE9C,SAAS,KAA8B;AACrC,SAAK,KAAK,KAAK,GAAG;AAClB,UAAM,QAAQ,IAAI,IAAI,CAAC,IAAI,MAAM,GAAG,IAAI,OAAO,EAAE,IAAI,CAAC,MAAM,iBAAiB,CAAC,CAAC,CAAC;AAChF,eAAW,SAAS,OAAO;AACzB,UAAI,CAAC,MAAO;AACZ,WAAK,QAAQ,IAAI,OAAO,GAAG;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,QAAQ,KAAuC;AAC7C,UAAM,MAAM,iBAAiB,GAAG;AAChC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;AAClC,QAAI,MAAO,QAAO;AAElB,UAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,CAAC,KAAK;AACrC,QAAI,CAAC,SAAS,UAAU,IAAK,QAAO;AACpC,WAAO,KAAK,QAAQ,IAAI,KAAK,KAAK;AAAA,EACpC;AAAA,EAEA,MAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAgC;AAC9B,WAAO,KAAK,KAAK,OAAO,CAAC,MAAM,EAAE,UAAU,UAAU,EAAE,eAAe,OAAO;AAAA,EAC/E;AAAA;AAAA,EAGA,YAAiC;AAC/B,WAAO,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS;AAAA,EAC9C;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/commands/render.js
CHANGED
|
@@ -20,23 +20,14 @@ function renderOfficial(result) {
|
|
|
20
20
|
return { msgType: 0, content: result.text };
|
|
21
21
|
}
|
|
22
22
|
function renderLlbot(result) {
|
|
23
|
-
if (!result.buttons
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
\u56DE\u590D\u6570\u5B57\u6267\u884C\uFF0860 \u79D2\u5185\u6709\u6548\uFF09`
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
const lines = result.buttons.map((b, i) => `[${i + 1}] ${b.label}`);
|
|
23
|
+
if (!result.buttons?.length) return { text: result.text };
|
|
24
|
+
const lines = result.buttons.map((button, index) => {
|
|
25
|
+
const detail = result.menu && button.description ? `
|
|
26
|
+
${button.description}` : "";
|
|
27
|
+
return `${index + 1}. ${button.label}${detail}`;
|
|
28
|
+
});
|
|
34
29
|
return {
|
|
35
|
-
text:
|
|
36
|
-
|
|
37
|
-
${lines.join("\n")}
|
|
38
|
-
|
|
39
|
-
\u56DE\u590D\u6570\u5B57\u6267\u884C\uFF0860 \u79D2\u5185\u6709\u6548\uFF09`
|
|
30
|
+
text: [result.text, "", result.menu ? "" : "\u53EF\u9009\u64CD\u4F5C", lines.join("\n"), "", "\u8BF7\u5728 60 \u79D2\u5185\u56DE\u590D\u7F16\u53F7\u9009\u62E9"].filter((line, index, all) => line !== "" || all[index - 1] !== "").join("\n")
|
|
40
31
|
};
|
|
41
32
|
}
|
|
42
33
|
export {
|