koishi-plugin-chatluna-toolbox 0.0.10 → 0.0.11

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.
Files changed (2) hide show
  1. package/lib/index.js +339 -51
  2. package/package.json +2 -2
package/lib/index.js CHANGED
@@ -47,9 +47,11 @@ __export(index_exports, {
47
47
  createXmlProcessor: () => createXmlProcessor,
48
48
  ensureOneBotSession: () => ensureOneBotSession,
49
49
  getSession: () => getSession,
50
+ hasReplyToolsEnabled: () => hasReplyToolsEnabled,
50
51
  inject: () => inject,
51
52
  name: () => name,
52
53
  parseSelfClosingXmlTags: () => parseSelfClosingXmlTags,
54
+ registerCharacterReplyTools: () => registerCharacterReplyTools,
53
55
  registerNativeTools: () => registerNativeTools,
54
56
  registerVariables: () => registerVariables,
55
57
  registerXmlTools: () => registerXmlTools,
@@ -57,7 +59,9 @@ __export(index_exports, {
57
59
  sendDeleteMessage: () => sendDeleteMessage,
58
60
  sendGroupBan: () => sendGroupBan,
59
61
  sendMsgEmoji: () => sendMsgEmoji,
60
- sendPoke: () => sendPoke
62
+ sendPoke: () => sendPoke,
63
+ sendSetGroupCard: () => sendSetGroupCard,
64
+ sendSetProfile: () => sendSetProfile
61
65
  });
62
66
  module.exports = __toCommonJS(index_exports);
63
67
 
@@ -195,6 +199,7 @@ var DEFAULT_XML_REFERENCE_PROMPT = `## \u52A8\u4F5C\u6307\u4EE4
195
199
  </actions>
196
200
  \`\`\``;
197
201
  var XmlToolsSchema = import_koishi3.Schema.object({
202
+ injectXmlToolAsReplyTool: import_koishi3.Schema.boolean().default(false).description("\u5C06 XML \u5DE5\u5177\u6539\u4E3A\u6CE8\u5165\u5B9E\u9A8C\u6027\u201C\u5DE5\u5177\u8C03\u7528\u56DE\u590D\u201D\u7684\u53C2\u6570\u4E2D"),
198
203
  enablePokeXmlTool: import_koishi3.Schema.boolean().default(false).description("\u542F\u7528 XML \u5F62\u5F0F\u7684\u6233\u4E00\u6233\u8C03\u7528\uFF08\u4E0E \u539F\u751F\u5DE5\u5177 \u4E8C\u9009\u4E00\uFF09"),
199
204
  enableEmojiXmlTool: import_koishi3.Schema.boolean().default(false).description(
200
205
  "\u542F\u7528 XML \u5F62\u5F0F\u7684\u6D88\u606F\u8868\u60C5\u8C03\u7528\uFF0C\uFF08\u9700 chatluna-character \u5F00\u542F enableMessageId\uFF0C\u4E0E \u539F\u751F\u5DE5\u5177 \u4E8C\u9009\u4E00\uFF0Cemoji_id \u5BF9\u7167\u8868\uFF1Ahttps://bot.q.qq.com/wiki/develop/pythonsdk/model/emoji.html \uFF09"
@@ -566,6 +571,34 @@ function createSetGroupBanTool(deps) {
566
571
  // src/features/native-tools/tools/set-group-card.ts
567
572
  var import_zod4 = require("zod");
568
573
  var import_tools3 = require("@langchain/core/tools");
574
+ async function sendSetGroupCard(params) {
575
+ try {
576
+ const { session, userId, card, groupId, log } = params;
577
+ if (!session) return "No session context available.";
578
+ const resolvedGroupId = groupId?.trim() || (session.guildId ? String(session.guildId).trim() : "") || (session.channelId ? String(session.channelId).trim() : "");
579
+ if (!resolvedGroupId) {
580
+ return "Missing groupId. Provide groupId explicitly or run inside a group session.";
581
+ }
582
+ const userIdRaw = userId.trim();
583
+ const cardRaw = card.trim();
584
+ if (!userIdRaw) return "userId is required.";
585
+ if (!cardRaw) return "card is required.";
586
+ const { error, internal } = ensureOneBotSession(session);
587
+ if (error) return error;
588
+ await callOneBotAPI(
589
+ internal,
590
+ "set_group_card",
591
+ { group_id: resolvedGroupId, user_id: userIdRaw, card: cardRaw },
592
+ ["setGroupCard"]
593
+ );
594
+ const message = `\u7FA4\u6635\u79F0\u5DF2\u66F4\u65B0\uFF1A${userIdRaw} -> ${cardRaw}`;
595
+ log?.("info", message);
596
+ return message;
597
+ } catch (error) {
598
+ params.log?.("warn", "set_group_card failed", error);
599
+ return `set_group_card failed: ${error.message}`;
600
+ }
601
+ }
569
602
  function createSetGroupCardTool(deps) {
570
603
  const { toolName, description, log } = deps;
571
604
  return new class extends import_tools3.StructuredTool {
@@ -577,31 +610,13 @@ function createSetGroupCardTool(deps) {
577
610
  card: import_zod4.z.string().min(1, "card is required").describe("New group card for the member.")
578
611
  });
579
612
  async _call(input, _manager, runnable) {
580
- try {
581
- const session = getSession(runnable);
582
- if (!session) return "No session context available.";
583
- const groupId = input.groupId?.trim() || (session.guildId ? String(session.guildId).trim() : "") || (session.channelId ? String(session.channelId).trim() : "");
584
- if (!groupId)
585
- return "Missing groupId. Provide groupId explicitly or run inside a group session.";
586
- const userId = input.userId.trim();
587
- const card = input.card.trim();
588
- if (!userId) return "userId is required.";
589
- if (!card) return "card is required.";
590
- const { error, internal } = ensureOneBotSession(session);
591
- if (error) return error;
592
- await callOneBotAPI(
593
- internal,
594
- "set_group_card",
595
- { group_id: groupId, user_id: userId, card },
596
- ["setGroupCard"]
597
- );
598
- const message = `\u7FA4\u6635\u79F0\u5DF2\u66F4\u65B0\uFF1A${userId} -> ${card}`;
599
- log?.("info", message);
600
- return message;
601
- } catch (error) {
602
- log?.("warn", "set_group_card failed", error);
603
- return `set_group_card failed: ${error.message}`;
604
- }
613
+ return sendSetGroupCard({
614
+ session: getSession(runnable),
615
+ groupId: input.groupId,
616
+ userId: input.userId,
617
+ card: input.card,
618
+ log
619
+ });
605
620
  }
606
621
  }();
607
622
  }
@@ -667,6 +682,25 @@ var genders = {
667
682
  male: "1",
668
683
  female: "2"
669
684
  };
685
+ async function sendSetProfile(params) {
686
+ try {
687
+ const { session, nickname, signature, gender, protocol, log } = params;
688
+ const { error, internal } = ensureOneBotSession(session);
689
+ if (error) return error;
690
+ const payload = { nickname };
691
+ if (signature) payload.personal_note = signature;
692
+ if (gender && protocol !== "llbot") payload.sex = genders[gender];
693
+ await callOneBotAPI(internal, "set_qq_profile", payload, [
694
+ "setQQProfile"
695
+ ]);
696
+ const message = "\u673A\u5668\u4EBA\u8D44\u6599\u5DF2\u66F4\u65B0\u3002";
697
+ log?.("info", message);
698
+ return message;
699
+ } catch (error) {
700
+ params.log?.("warn", "\u4FEE\u6539\u673A\u5668\u4EBA\u8D26\u6237\u4FE1\u606F\u5931\u8D25", error);
701
+ return `\u4FEE\u6539\u673A\u5668\u4EBA\u8D26\u6237\u4FE1\u606F\u5931\u8D25\uFF1A${error.message}`;
702
+ }
703
+ }
670
704
  function createSetProfileTool(deps) {
671
705
  const { toolName, description, log, protocol } = deps;
672
706
  return new class extends import_tools5.StructuredTool {
@@ -678,24 +712,14 @@ function createSetProfileTool(deps) {
678
712
  gender: import_zod6.z.enum(["unknown", "male", "female"]).optional().describe("Optional: the new gender.")
679
713
  });
680
714
  async _call(input, _manager, runnable) {
681
- try {
682
- const session = getSession(runnable);
683
- const { error, internal } = ensureOneBotSession(session);
684
- if (error) return error;
685
- const payload = { nickname: input.nickname };
686
- if (input.signature) payload.personal_note = input.signature;
687
- if (input.gender && protocol !== "llbot")
688
- payload.sex = genders[input.gender];
689
- await callOneBotAPI(internal, "set_qq_profile", payload, [
690
- "setQQProfile"
691
- ]);
692
- const message = "\u673A\u5668\u4EBA\u8D44\u6599\u5DF2\u66F4\u65B0\u3002";
693
- log?.("info", message);
694
- return message;
695
- } catch (error) {
696
- log?.("warn", "\u4FEE\u6539\u673A\u5668\u4EBA\u8D26\u6237\u4FE1\u606F\u5931\u8D25", error);
697
- return `\u4FEE\u6539\u673A\u5668\u4EBA\u8D26\u6237\u4FE1\u606F\u5931\u8D25\uFF1A${error.message}`;
698
- }
715
+ return sendSetProfile({
716
+ session: getSession(runnable),
717
+ nickname: input.nickname,
718
+ signature: input.signature,
719
+ gender: input.gender,
720
+ protocol,
721
+ log
722
+ });
699
723
  }
700
724
  }();
701
725
  }
@@ -816,6 +840,253 @@ function registerNativeTools(deps) {
816
840
  }
817
841
  }
818
842
 
843
+ // src/features/reply-tools/register.ts
844
+ function escapeAttr(value) {
845
+ return String(value ?? "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
846
+ }
847
+ function hasReplyToolsEnabled(config) {
848
+ return !!(config.injectXmlToolAsReplyTool && (config.enablePokeXmlTool || config.enableBanXmlTool || config.enableEmojiXmlTool || config.enableDeleteXmlTool));
849
+ }
850
+ function registerCharacterReplyTools(deps) {
851
+ const { ctx, config, protocol, log } = deps;
852
+ const service = ctx.chatluna_character;
853
+ if (!service?.registerReplyToolField) {
854
+ return () => {
855
+ };
856
+ }
857
+ const disposers = [];
858
+ if (config.injectXmlToolAsReplyTool && config.enablePokeXmlTool) {
859
+ disposers.push(
860
+ service.registerReplyToolField({
861
+ name: "toolbox_poke",
862
+ schema: {
863
+ type: "array",
864
+ description: "Poke one or more users after sending this reply. Each item is one poke action.",
865
+ items: {
866
+ type: "object",
867
+ properties: {
868
+ user_id: {
869
+ type: "string",
870
+ description: "Target user ID to poke."
871
+ },
872
+ group_id: {
873
+ type: "string",
874
+ description: "Optional target group ID. Defaults to the current session group."
875
+ }
876
+ },
877
+ required: ["user_id"]
878
+ }
879
+ },
880
+ async invoke(_, session, value) {
881
+ if (!Array.isArray(value)) return;
882
+ for (const item of value) {
883
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
884
+ continue;
885
+ }
886
+ const action = item;
887
+ if (typeof action.user_id !== "string") continue;
888
+ await sendPoke({
889
+ session,
890
+ userId: action.user_id,
891
+ groupId: typeof action.group_id === "string" ? action.group_id : void 0,
892
+ protocol,
893
+ log
894
+ });
895
+ }
896
+ },
897
+ render(_, __, value) {
898
+ if (!Array.isArray(value)) return;
899
+ return value.flatMap((item) => {
900
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
901
+ return [];
902
+ }
903
+ const action = item;
904
+ if (typeof action.user_id !== "string") return [];
905
+ const group = typeof action.group_id === "string" && action.group_id.trim() ? ` group_id="${escapeAttr(action.group_id)}"` : "";
906
+ return [`<poke id="${escapeAttr(action.user_id)}"${group} />`];
907
+ });
908
+ }
909
+ })
910
+ );
911
+ }
912
+ if (config.injectXmlToolAsReplyTool && config.enableBanXmlTool) {
913
+ disposers.push(
914
+ service.registerReplyToolField({
915
+ name: "toolbox_set_group_ban",
916
+ schema: {
917
+ type: "array",
918
+ description: "Mute or unmute one or more users after this reply. Each item is one group ban action.",
919
+ items: {
920
+ type: "object",
921
+ properties: {
922
+ user_id: {
923
+ type: "string",
924
+ description: "Target member user ID."
925
+ },
926
+ duration: {
927
+ type: "number",
928
+ description: "Mute duration in seconds. Use 0 to unmute the member."
929
+ },
930
+ group_id: {
931
+ type: "string",
932
+ description: "Optional target group ID. Defaults to the current session group."
933
+ }
934
+ },
935
+ required: ["user_id", "duration"]
936
+ }
937
+ },
938
+ async invoke(_, session, value) {
939
+ if (!Array.isArray(value)) return;
940
+ for (const item of value) {
941
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
942
+ continue;
943
+ }
944
+ const action = item;
945
+ if (typeof action.user_id !== "string") continue;
946
+ await sendGroupBan({
947
+ session,
948
+ userId: action.user_id,
949
+ duration: typeof action.duration === "number" || typeof action.duration === "string" ? action.duration : "",
950
+ groupId: typeof action.group_id === "string" ? action.group_id : void 0,
951
+ protocol,
952
+ log
953
+ });
954
+ }
955
+ },
956
+ render(_, __, value) {
957
+ if (!Array.isArray(value)) return;
958
+ return value.flatMap((item) => {
959
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
960
+ return [];
961
+ }
962
+ const action = item;
963
+ if (typeof action.user_id !== "string") return [];
964
+ const duration = typeof action.duration === "number" || typeof action.duration === "string" ? String(action.duration) : "";
965
+ if (!duration) return [];
966
+ const group = typeof action.group_id === "string" && action.group_id.trim() ? ` group_id="${escapeAttr(action.group_id)}"` : "";
967
+ return [
968
+ `<ban id="${escapeAttr(action.user_id)}" duration="${escapeAttr(duration)}"${group} />`
969
+ ];
970
+ });
971
+ }
972
+ })
973
+ );
974
+ }
975
+ if (config.injectXmlToolAsReplyTool && config.enableEmojiXmlTool) {
976
+ disposers.push(
977
+ service.registerReplyToolField({
978
+ name: "toolbox_set_msg_emoji",
979
+ schema: {
980
+ type: "array",
981
+ description: "Add emoji reactions to one or more messages after this reply. Each item is one emoji action.",
982
+ items: {
983
+ type: "object",
984
+ properties: {
985
+ message_id: {
986
+ type: "string",
987
+ description: "Target message ID."
988
+ },
989
+ emoji_id: {
990
+ type: "string",
991
+ description: "Emoji ID to send."
992
+ }
993
+ },
994
+ required: ["message_id", "emoji_id"]
995
+ }
996
+ },
997
+ async invoke(_, session, value) {
998
+ if (!Array.isArray(value)) return;
999
+ for (const item of value) {
1000
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
1001
+ continue;
1002
+ }
1003
+ const action = item;
1004
+ if (typeof action.message_id !== "string" || typeof action.emoji_id !== "string") {
1005
+ continue;
1006
+ }
1007
+ await sendMsgEmoji({
1008
+ session,
1009
+ messageId: action.message_id,
1010
+ emojiId: action.emoji_id,
1011
+ protocol,
1012
+ log
1013
+ });
1014
+ }
1015
+ },
1016
+ render(_, __, value) {
1017
+ if (!Array.isArray(value)) return;
1018
+ return value.flatMap((item) => {
1019
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
1020
+ return [];
1021
+ }
1022
+ const action = item;
1023
+ if (typeof action.message_id !== "string" || typeof action.emoji_id !== "string") {
1024
+ return [];
1025
+ }
1026
+ return [
1027
+ `<emoji message_id="${escapeAttr(action.message_id)}" emoji_id="${escapeAttr(action.emoji_id)}" />`
1028
+ ];
1029
+ });
1030
+ }
1031
+ })
1032
+ );
1033
+ }
1034
+ if (config.injectXmlToolAsReplyTool && config.enableDeleteXmlTool) {
1035
+ disposers.push(
1036
+ service.registerReplyToolField({
1037
+ name: "toolbox_delete_message",
1038
+ schema: {
1039
+ type: "array",
1040
+ description: "Delete one or more messages after this reply. Each item is one delete action.",
1041
+ items: {
1042
+ type: "object",
1043
+ properties: {
1044
+ message_id: {
1045
+ type: "string",
1046
+ description: "Target message ID to delete."
1047
+ }
1048
+ },
1049
+ required: ["message_id"]
1050
+ }
1051
+ },
1052
+ async invoke(_, session, value) {
1053
+ if (!Array.isArray(value)) return;
1054
+ for (const item of value) {
1055
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
1056
+ continue;
1057
+ }
1058
+ const action = item;
1059
+ if (typeof action.message_id !== "string") continue;
1060
+ await sendDeleteMessage({
1061
+ session,
1062
+ messageId: action.message_id,
1063
+ log
1064
+ });
1065
+ }
1066
+ },
1067
+ render(_, __, value) {
1068
+ if (!Array.isArray(value)) return;
1069
+ return value.flatMap((item) => {
1070
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
1071
+ return [];
1072
+ }
1073
+ const action = item;
1074
+ if (typeof action.message_id !== "string") return [];
1075
+ return [
1076
+ `<delete message_id="${escapeAttr(action.message_id)}" />`
1077
+ ];
1078
+ });
1079
+ }
1080
+ })
1081
+ );
1082
+ }
1083
+ return () => {
1084
+ for (const dispose of disposers.reverse()) {
1085
+ dispose();
1086
+ }
1087
+ };
1088
+ }
1089
+
819
1090
  // src/features/xml-tools/parser.ts
820
1091
  function parseSelfClosingXmlTags(text, tagName) {
821
1092
  const tags = Array.from(
@@ -1875,34 +2146,47 @@ function apply(ctx, config) {
1875
2146
  }
1876
2147
  };
1877
2148
  const log = createLogger(ctx, config);
2149
+ const enableXmlRuntime = !config.injectXmlToolAsReplyTool && (config.enablePokeXmlTool || config.enableEmojiXmlTool || config.enableDeleteXmlTool || config.enableBanXmlTool);
1878
2150
  let xmlRuntime = null;
1879
2151
  let characterCtx = null;
2152
+ let replyToolsDispose = null;
1880
2153
  const initializeServices = async () => {
1881
2154
  log("info", "toolbox \u521D\u59CB\u5316\u5F00\u59CB");
1882
2155
  registerVariables({ ctx, config, log });
1883
2156
  const protocol = resolveOneBotProtocol(config, log);
1884
2157
  registerNativeTools({ ctx, config, plugin, protocol, log });
1885
- xmlRuntime = registerXmlTools({ ctx, config, protocol, log });
1886
- if (characterCtx && xmlRuntime.start()) {
2158
+ xmlRuntime = enableXmlRuntime ? registerXmlTools({ ctx, config, protocol, log }) : null;
2159
+ if (characterCtx && xmlRuntime?.start()) {
1887
2160
  log("info", "XML \u5DE5\u5177\u5DF2\u542F\u7528");
1888
2161
  }
1889
2162
  log("info", "toolbox \u521D\u59CB\u5316\u5B8C\u6210");
1890
2163
  };
1891
2164
  const dispose = () => {
1892
2165
  characterCtx = null;
2166
+ replyToolsDispose?.();
2167
+ replyToolsDispose = null;
1893
2168
  xmlRuntime?.stop();
1894
2169
  xmlRuntime = null;
1895
2170
  };
1896
- if (config.enablePokeXmlTool || config.enableEmojiXmlTool || config.enableDeleteXmlTool || config.enableBanXmlTool) {
2171
+ if (hasReplyToolsEnabled(config) || enableXmlRuntime) {
1897
2172
  ctx.inject(["chatluna_character"], (innerCtx) => {
1898
2173
  characterCtx = innerCtx;
1899
- if (!xmlRuntime) return;
1900
- const started = xmlRuntime.start();
1901
- if (started) log("info", "XML \u5DE5\u5177\u5DF2\u542F\u7528");
2174
+ replyToolsDispose?.();
2175
+ replyToolsDispose = hasReplyToolsEnabled(config) ? registerCharacterReplyTools({
2176
+ ctx: innerCtx,
2177
+ config,
2178
+ protocol: resolveOneBotProtocol(config, log),
2179
+ log
2180
+ }) : null;
1902
2181
  innerCtx.on("dispose", () => {
1903
2182
  if (characterCtx === innerCtx) characterCtx = null;
2183
+ replyToolsDispose?.();
2184
+ replyToolsDispose = null;
1904
2185
  xmlRuntime?.stop();
1905
2186
  });
2187
+ if (!xmlRuntime) return;
2188
+ const started = xmlRuntime.start();
2189
+ if (started) log("info", "XML \u5DE5\u5177\u5DF2\u542F\u7528");
1906
2190
  });
1907
2191
  }
1908
2192
  ctx.on("dispose", dispose);
@@ -1943,9 +2227,11 @@ function apply(ctx, config) {
1943
2227
  createXmlProcessor,
1944
2228
  ensureOneBotSession,
1945
2229
  getSession,
2230
+ hasReplyToolsEnabled,
1946
2231
  inject,
1947
2232
  name,
1948
2233
  parseSelfClosingXmlTags,
2234
+ registerCharacterReplyTools,
1949
2235
  registerNativeTools,
1950
2236
  registerVariables,
1951
2237
  registerXmlTools,
@@ -1953,5 +2239,7 @@ function apply(ctx, config) {
1953
2239
  sendDeleteMessage,
1954
2240
  sendGroupBan,
1955
2241
  sendMsgEmoji,
1956
- sendPoke
2242
+ sendPoke,
2243
+ sendSetGroupCard,
2244
+ sendSetProfile
1957
2245
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chatluna-toolbox",
3
3
  "description": "为 ChatLuna 提供更多原生工具、XML 工具与变量,仅支持 onebot 平台。",
4
- "version": "0.0.10",
4
+ "version": "0.0.11",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "files": [
@@ -42,7 +42,7 @@
42
42
  "scripts": {
43
43
  "build": "tsup",
44
44
  "watch": "tsup --watch",
45
- "typecheck": "tsc --noEmit",
45
+ "typecheck": "NODE_OPTIONS=--max-old-space-size=8192 tsc --noEmit",
46
46
  "test": "vitest run"
47
47
  }
48
48
  }