koishi-plugin-satori-ai 1.3.7 → 1.3.9

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/lib/index.d.ts CHANGED
@@ -35,7 +35,7 @@ export declare class SAT extends Sat {
35
35
  content: string;
36
36
  error: boolean;
37
37
  }>;
38
- getChatResponse(user: User, messages: Sat.Msg[]): Promise<{
38
+ getChatResponse(user: User, messages: Sat.Msg[], prompt: string): Promise<{
39
39
  content: string;
40
40
  error: boolean;
41
41
  }>;
@@ -47,9 +47,10 @@ export declare class SAT extends Sat {
47
47
  getWarningList(session: Session): Promise<void>;
48
48
  private clearSession;
49
49
  private addCommonSense;
50
+ addGroupCommonSense(session: Session, content: string, groupId: string): Promise<string>;
50
51
  private handleUserLevel;
51
52
  private handleUserUsage;
52
- handleRandomMiddleware(session: Session, prompt: string): Promise<string>;
53
+ handleRandomMiddleware(session: Session): Promise<string>;
53
54
  handleNickNameMiddleware(session: Session, prompt: string): Promise<string>;
54
55
  handleChannelMemoryManager(session: Session): Promise<void>;
55
56
  }
package/lib/index.js CHANGED
@@ -46,8 +46,8 @@ __export(src_exports, {
46
46
  refreshPuppeteer: () => refreshPuppeteer
47
47
  });
48
48
  module.exports = __toCommonJS(src_exports);
49
- var import_koishi14 = require("koishi");
50
- var path7 = __toESM(require("path"));
49
+ var import_koishi13 = require("koishi");
50
+ var path6 = __toESM(require("path"));
51
51
 
52
52
  // src/api.ts
53
53
  var import_koishi2 = require("koishi");
@@ -258,25 +258,36 @@ function processPrompt(prompt) {
258
258
  return prompt.trim();
259
259
  }
260
260
  __name(processPrompt, "processPrompt");
261
- function filterResponse(prompt, words) {
262
- const parts = prompt.split(/([(\[【《(][^))]*[)\]】》)])/g);
263
- const filtered = parts.map((part) => {
264
- if (part.startsWith("(") && part.endsWith(")") || part.startsWith("(") && part.endsWith(")") || part.startsWith("[") && part.endsWith("]") || part.startsWith("【") && part.endsWith("】") || part.startsWith("《") && part.endsWith("》")) {
265
- return words.some((word) => part.includes(word)) ? "" : part;
266
- }
267
- return part;
268
- }).join("");
269
- const cleaned = filtered.replace(/\s+/g, "");
270
- const answerRegex = /<p>((?!<\/?p>)[\s\S])*?<\/p/g;
271
- const answerRegex2 = /<doubaothinking>((?!<\/?doubaothinking>)[\s\S])*?<\/p/g;
261
+ function filterResponse(prompt, words, options) {
262
+ const applyBracketFilter = options?.applyBracketFilter ?? true;
263
+ const applyTagFilter = options?.applyTagFilter ?? true;
264
+ let working = prompt;
265
+ if (applyBracketFilter) {
266
+ const parts = working.split(/([(\[【《(][^))]*[)\]】》)])/g);
267
+ const filtered = parts.map((part) => {
268
+ if (part.startsWith("(") && part.endsWith(")") || part.startsWith("(") && part.endsWith(")") || part.startsWith("[") && part.endsWith("]") || part.startsWith("【") && part.endsWith("】") || part.startsWith("《") && part.endsWith("》")) {
269
+ return words.some((word) => part.includes(word)) ? "" : part;
270
+ }
271
+ return part;
272
+ }).join("");
273
+ working = filtered.replace(/\s+/g, "");
274
+ if (!working) {
275
+ working = prompt;
276
+ }
277
+ }
278
+ if (!applyTagFilter) {
279
+ return working ? { content: working, error: false } : { content: "有点问题,请重置对话", error: true };
280
+ }
281
+ const answerRegex = /<p>((?!<\/?p>)[\s\S])*?<\/p>/g;
282
+ const answerRegex2 = /<doubaothinking>((?!<\/?doubaothinking>)[\s\S])*?<\/p>/g;
272
283
  const answerRegex3 = /<doubaothinking>((?!<\/?doubaothinking>)[\s\S])*?<\/doubaothinking>/g;
273
284
  const answerRegex4 = /<answer>((?!<\/?answer>)[\s\S])*?<\/answer>/g;
274
285
  const answerRegex5 = /<doubaothinking>((?!<\/?doubaothinking>)[\s\S])*?<\/answer>/g;
275
- const answerMatches = cleaned.match(answerRegex);
276
- const answerMatches2 = cleaned.match(answerRegex2);
277
- const answerMatches3 = cleaned.match(answerRegex3);
278
- const answerMatches4 = cleaned.match(answerRegex4);
279
- const answerMatches5 = cleaned.match(answerRegex5);
286
+ const answerMatches = working.match(answerRegex);
287
+ const answerMatches2 = working.match(answerRegex2);
288
+ const answerMatches3 = working.match(answerRegex3);
289
+ const answerMatches4 = working.match(answerRegex4);
290
+ const answerMatches5 = working.match(answerRegex5);
280
291
  const lastAnswer = answerMatches ? answerMatches[answerMatches.length - 1] : null;
281
292
  const lastDoubao = answerMatches2 ? answerMatches2[answerMatches2.length - 1] : null;
282
293
  const lastAnswer3 = answerMatches3 ? answerMatches3[answerMatches3.length - 1] : null;
@@ -347,6 +358,12 @@ function countCommonChars(str1, str2) {
347
358
  const count2 = chars2.get(char) || 0;
348
359
  commonCount += Math.min(count1, count2);
349
360
  }
361
+ const countDigits = /* @__PURE__ */ __name((s) => (s.match(/\d/g) || []).length, "countDigits");
362
+ const digits1 = countDigits(str1);
363
+ const digits2 = countDigits(str2);
364
+ if (digits1 >= 3 || digits2 >= 3) {
365
+ return Math.max(commonCount, 2);
366
+ }
350
367
  return commonCount;
351
368
  }
352
369
  __name(countCommonChars, "countCommonChars");
@@ -451,7 +468,7 @@ var APIClient = class {
451
468
  if (i == this.config.maxRetryTimes) {
452
469
  return this.handleAPIError(error);
453
470
  }
454
- await new Promise((resolve3) => setTimeout(resolve3, this.config.retry_delay_time || 5e3));
471
+ await new Promise((resolve2) => setTimeout(resolve2, this.config.retry_delay_time || 5e3));
455
472
  logger2.warn(`生成画像时API请求失败(${error}),重试(第${i}次)中...`);
456
473
  continue;
457
474
  }
@@ -508,7 +525,7 @@ var APIClient = class {
508
525
  if (i == this.config.maxRetryTimes) {
509
526
  return this.handleAPIError(error);
510
527
  }
511
- await new Promise((resolve3) => setTimeout(resolve3, this.config.retry_delay_time || 5e3));
528
+ await new Promise((resolve2) => setTimeout(resolve2, this.config.retry_delay_time || 5e3));
512
529
  logger2.warn(`API请求失败(${error}),重试(第${i}次)中...`);
513
530
  continue;
514
531
  }
@@ -582,7 +599,7 @@ var MemoryManager = class {
582
599
  }
583
600
  channelMemories = /* @__PURE__ */ new Map();
584
601
  channelDialogues = /* @__PURE__ */ new Map();
585
- charactersToRemove = ["的", "一", "是", "了", "什", "么", "我", "谁", "不", "人", "在", "他", "有", "这", "个", "上", "们", "来", "到", "时", "大", "地", "为", "子", "中", "你", "说", "生", "国", "年", "着", "就", "那", "和", "要", "她", "出", "也", "得", "里", "后", "自", "以", "会", "id=", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
602
+ charactersToRemove = ["的", "一", "是", "了", "什", "么", "我", "谁", "不", "人", "在", "他", "有", "这", "个", "上", "们", "来", "到", "时", "大", "地", "为", "子", "中", "你", "说", "生", "国", "年", "着", "就", "那", "和", "要", "她", "出", "也", "得", "里", "后", "自", "以", "会", "id="];
586
603
  MAX_MEMORY_LENGTH = 5e3;
587
604
  // 更新记忆
588
605
  async updateMemories(session, prompt, config, response) {
@@ -617,7 +634,7 @@ var MemoryManager = class {
617
634
  if (!this.config.channel_dialogues)
618
635
  return "";
619
636
  const Dialogue = this.channelDialogues[session.channelId]?.join("\n") || "";
620
- const result = "以下是群聊内群友最近的聊天记录,当用户话题涉及其中内容时你可以参考这些信息:{\n" + Dialogue + "\n}\n";
637
+ const result = "以下是群聊内最近的包括所有人的聊天记录,当当前对话涉及其中内容时你可以参考这些信息,但是要注意分辨发言人是谁:{\n" + Dialogue + "\n}\n";
621
638
  return result;
622
639
  }
623
640
  // 括号引号过滤
@@ -712,11 +729,14 @@ var MemoryManager = class {
712
729
  async searchMemories(session, prompt, type = "user") {
713
730
  const filePathMap = {
714
731
  "user": this.getUserMemoryPath(session.userId),
715
- "common": path2.join(this.config.dataDir, "common_sense.txt")
732
+ "common": path2.join(this.config.dataDir, "common_sense.txt"),
733
+ "group": path2.join(this.config.dataDir, "group_sense", `${session.channelId}.txt`)
716
734
  };
717
735
  const topNMap = {
718
736
  "user": this.config.dailogues_topN,
719
- "common": this.config.common_topN
737
+ "common": this.config.common_topN,
738
+ "group": this.config.common_topN
739
+ // 群常识和常识使用同样的topN
720
740
  };
721
741
  const filePath = filePathMap[type];
722
742
  if (!fs2.existsSync(filePath)) {
@@ -753,7 +773,8 @@ var MemoryManager = class {
753
773
  formatMatches(matched, type, topN = 5) {
754
774
  const prefixMap = {
755
775
  "common": "这是你可能用到的信息:",
756
- "user": "以下是较久之前用户说过的话和对话时间:"
776
+ "user": "以下是较久之前用户说过的话和对话时间:",
777
+ "group": "以下是本群聊你可能需要知道的信息:"
757
778
  };
758
779
  const time = `时段:${getTimeOfDay((/* @__PURE__ */ new Date()).getHours())}`;
759
780
  const date = `当前日期和时间:${(/* @__PURE__ */ new Date()).toLocaleString()} ${time}`;
@@ -766,11 +787,17 @@ ${date}
766
787
  }
767
788
  `;
768
789
  return result;
769
- } else {
790
+ } else if (type === "user") {
770
791
  matched.forEach((entry) => entry.content = entry.content + (entry.role === "user" ? "(未记录时间)" : entry.role));
771
792
  const result = `${prefixMap[type]}{
772
793
  ${matched.map((entry) => entry.content).join("\n")}
773
794
  }
795
+ `;
796
+ return result;
797
+ } else if (type === "group") {
798
+ const result = `${prefixMap[type]}{
799
+ ${matched.map((entry) => entry.content).join("\n")}
800
+ }
774
801
  `;
775
802
  return result;
776
803
  }
@@ -919,6 +946,8 @@ async function handleFavorabilitySystem(ctx, session, config) {
919
946
  }
920
947
  __name(handleFavorabilitySystem, "handleFavorabilitySystem");
921
948
  function getFavorabilityLevel(user, config) {
949
+ if (user.favorability >= config.favorability_div_3 && user?.items["镇定贴"]?.count > 0 && user?.items["镇定贴"]?.description && user?.items["镇定贴"]?.description == "on")
950
+ return "朋友";
922
951
  if (user?.items["订婚戒指"]?.count > 0 && user?.items["订婚戒指"]?.description && user?.items["订婚戒指"]?.description == "已使用")
923
952
  return "夫妻";
924
953
  if (user.favorability < config.favorability_div_1)
@@ -1068,7 +1097,7 @@ function createMiddleware(ctx, sat, config) {
1068
1097
  return await handleNickNameMessage(sat, session);
1069
1098
  }
1070
1099
  if (shouldRandomTrigger(session, config)) {
1071
- return await sat.handleRandomMiddleware(session, session.content);
1100
+ return await sat.handleRandomMiddleware(session);
1072
1101
  }
1073
1102
  return next();
1074
1103
  };
@@ -1139,6 +1168,7 @@ var Sat = class extends import_koishi5.Service {
1139
1168
  import_koishi5.Schema.transform(String, (value) => [value])
1140
1169
  ]).default([]).role("secret").description("深度思考模型api_key"),
1141
1170
  appointModel: import_koishi5.Schema.string().default("deepseek-reasoner").description("深度思考模型"),
1171
+ enhanceReasoningProtection: import_koishi5.Schema.boolean().default(true).description("是否增强思维链保护(开启后会强制输出内容带有限制标签,极大防止思维链溢出问题)"),
1142
1172
  not_reasoner_LLM_URL: import_koishi5.Schema.string().default("https://api.deepseek.com").description("非深度思考模型请求地址"),
1143
1173
  not_reasoner_LLM: import_koishi5.Schema.string().default("deepseek-chat").description("非深度思考模型(用于节省成本"),
1144
1174
  not_reasoner_LLM_key: import_koishi5.Schema.union([
@@ -1182,7 +1212,7 @@ var Sat = class extends import_koishi5.Service {
1182
1212
  dataDir: import_koishi5.Schema.string().default("./data/satori_ai").description("聊天记录保存位置(长期记忆)"),
1183
1213
  memory_block_words: import_koishi5.Schema.array(String).default(["好感"]).description("记忆屏蔽词"),
1184
1214
  remember_min_length: import_koishi5.Schema.number().description("触发保存到记忆的长度").default(20),
1185
- common_topN: import_koishi5.Schema.number().default(5).description("常识记忆检索最大匹配数"),
1215
+ common_topN: import_koishi5.Schema.number().default(5).description("群常识、常识记忆检索最大匹配数"),
1186
1216
  dailogues_topN: import_koishi5.Schema.number().default(5).description("对话记忆检索最大匹配数"),
1187
1217
  enable_fixed_dialogues: import_koishi5.Schema.boolean().default(false).description("是否启用固定对话(在dataDir中的fixed_dialogues.json修改)")
1188
1218
  }).description("记忆设置"),
@@ -1197,6 +1227,7 @@ var Sat = class extends import_koishi5.Service {
1197
1227
  duplicateDialogueCheck: import_koishi5.Schema.boolean().default(true).description("是否检查重复对话"),
1198
1228
  enable_online_user_check: import_koishi5.Schema.boolean().default(true).description("在未回答而再次提问时是否提示用户有未完成的对话"),
1199
1229
  random_min_tokens: import_koishi5.Schema.number().default(20).description("随机触发对话的最小长度"),
1230
+ enable_random_without_favorability: import_koishi5.Schema.boolean().default(true).description("是否启用随机触发对话无视高好感度(即使用户好感度很高也会最多视为朋友)"),
1200
1231
  randnum: import_koishi5.Schema.number().role("slider").min(0).max(1).step(0.01).default(0).description("在群聊中随机触发对话的概率,如需关闭可设置为 0"),
1201
1232
  sentences_divide: import_koishi5.Schema.boolean().default(true).description("是否分句发送"),
1202
1233
  min_sentences_length: import_koishi5.Schema.number().default(10).description("每个分句的最小长度"),
@@ -1255,9 +1286,6 @@ var Sat = class extends import_koishi5.Service {
1255
1286
  import_koishi5.Schema.object({
1256
1287
  enable_game: import_koishi5.Schema.boolean().default(false).description("是否开启游戏模块"),
1257
1288
  game_block_channel: import_koishi5.Schema.array(String).description("不提供游戏的频道id"),
1258
- enable_gobang: import_koishi5.Schema.boolean().default(false).description("是否开启五子棋游戏(需要puppeteer支持)"),
1259
- channel_id_for_gobang: import_koishi5.Schema.array(String).description("启用五子棋的游戏频道id(不建议太多,第一个会被公示)"),
1260
- cd_for_gobang: import_koishi5.Schema.number().default(3600).description("每人五子棋游戏cd时间(秒)"),
1261
1289
  enable_OneTouch: import_koishi5.Schema.boolean().default(false).description("是否开启一碰一游戏(需要puppeteer支持)"),
1262
1290
  cd_for_OneTouch: import_koishi5.Schema.number().default(600).description("每人一碰一游戏cd时间(秒)"),
1263
1291
  enable_fencing: import_koishi5.Schema.boolean().default(false).description("是否开启击剑游戏(无效,开发中)")
@@ -1309,7 +1337,7 @@ var UserPortraitManager = class {
1309
1337
  });
1310
1338
  fs5.writeFileSync(memoryPath, JSON.stringify(replacedContent, null, 2));
1311
1339
  const validEntries = replacedContent.filter((entry) => entry.role !== "user").map((entry) => {
1312
- const timeMatch = entry.role.match(/对话日期和时间:(\d{4}\/\d{1,2}\/\d{1,2} \d{2}:\d{2}:\d{2})/);
1340
+ const timeMatch = entry.role.match(/对话日期和时间:(\d{1,2}\/\d{1,2}\/\d{4}), (\d{1,2}:\d{2}:\d{2} [AP]M)/);
1313
1341
  if (!timeMatch)
1314
1342
  return null;
1315
1343
  return {
@@ -1323,11 +1351,11 @@ var UserPortraitManager = class {
1323
1351
  return validEntries.slice(0, maxEntries).map((entry) => `${entry.content}${entry.role}`);
1324
1352
  }
1325
1353
  // 生成提示词模板
1326
- buildMessage(level, history, existingPortrait, user) {
1354
+ buildFirstMessage(level, history, user) {
1327
1355
  return [{
1328
1356
  role: "user",
1329
- content: `你是一个角色扮演智能体工作流中的一环,请根据以下信息更新用户画像。需包括:
1330
- 1. 基本信息(性别、年龄、生日等,不要遗漏或改动已有的信息)
1357
+ content: `你是一个角色扮演智能体工作流中的一环,请根据以下信息生成户画像。可选内容包括:
1358
+ 1. 基本信息(性别、年龄、生日等)
1331
1359
  2. 生活习惯(作息等)
1332
1360
  3. 反感点(如有)
1333
1361
  4. 对AI的期望(从对话历史中推断)
@@ -1335,21 +1363,47 @@ var UserPortraitManager = class {
1335
1363
 
1336
1364
  用户名:${user.usersname}
1337
1365
  你与用户的关系:${level}
1338
- 历史画像:
1339
- ${existingPortrait ? "无" : existingPortrait}
1340
-
1341
1366
  近期的用户发言记录:
1342
1367
  ${history}
1343
1368
 
1344
1369
  注意事项:
1370
+ ·没有涉及到的信息注为无
1345
1371
  ·用户一定是人类,其他情况是角色扮演
1346
- ·根据发言修正历史画像内容,但是历史画像中与发言无关的部分必须保留
1347
1372
  ·尤其注意用户说“记住”的部分,可能是用户希望记录的信息
1348
1373
  ·因为在角色扮演中,用户可能会说出不符合事实的信息,需要根据事实推断,避免盲目相信或主观臆测
1349
1374
  ·使用尽可能简洁的表达
1350
1375
  ·保留不确定性的表述(如"可能"、"似乎"、"用户自称")
1351
1376
  ·保持中立和客观,避免带有个人情感色彩的描述,不要添加评价或建议
1352
1377
  ·仅给出画像内容,不要添加额外的描述、建议、评价、注解等任何内容
1378
+ ·不使用markdown等标记语言,直接书写即可`
1379
+ }];
1380
+ }
1381
+ buildSecondMessage(level, NewPortrait, existingPortrait, user) {
1382
+ return [{
1383
+ role: "user",
1384
+ content: `你是一个角色扮演智能体工作流中的一环,请根据以下信息合并用户画像。内容包括:
1385
+ 1. 基本信息(性别、年龄、生日等,不要遗漏或改动已有的信息)
1386
+ 2. 生活习惯(作息等)
1387
+ 3. 反感点(如有)
1388
+ 4. 对AI的期望(从对话历史中推断)
1389
+ 5. 用户希望记住的信息(如有)
1390
+
1391
+ 用户名:${user.usersname}
1392
+ 你与用户的关系:${level}
1393
+ 已有画像:
1394
+ ${existingPortrait ? "无" : existingPortrait}
1395
+
1396
+ 新补充信息:
1397
+ ${NewPortrait}
1398
+
1399
+ 注意事项:
1400
+ ·用户一定是人类,其他情况是角色扮演
1401
+ ·根据已有画像,补充新信息,避免重复,并且不要遗漏你认为重要信息
1402
+ ·你认为不太重要的信息可以删除
1403
+ ·使用尽可能简洁的表达
1404
+ ·保留不确定性的表述(如"可能"、"似乎"、"用户自称")
1405
+ ·保持中立和客观,避免带有个人情感色彩的描述,不要添加评价或建议
1406
+ ·仅给出画像内容,不要添加额外的描述、建议、评价、注解等任何内容
1353
1407
  ·不使用markdown等标记语言,直接书写即可`
1354
1408
  }];
1355
1409
  }
@@ -1364,10 +1418,12 @@ ${history}
1364
1418
  const dialogues = await this.getDialogues(user);
1365
1419
  const existing = this.readPortrait(user.userid);
1366
1420
  const userlevel = getFavorabilityLevel(user, this.getFavorabilityConfig());
1367
- const messages = this.buildMessage(userlevel, dialogues.join("\n"), existing, user);
1368
1421
  logger5.info(`用户 ${user.userid} 画像生成中...`);
1369
1422
  try {
1370
- const response = await apiClient.generateUserPortrait(user, messages);
1423
+ const FirstMessages = this.buildFirstMessage(userlevel, dialogues.join("\n"), user);
1424
+ const NewPortrait = await apiClient.generateUserPortrait(user, FirstMessages);
1425
+ const SecondMessages = this.buildSecondMessage(userlevel, NewPortrait.content, existing, user);
1426
+ const response = await apiClient.generateUserPortrait(user, SecondMessages);
1371
1427
  if (response && !response.error) {
1372
1428
  this.savePortrait(user, response.content);
1373
1429
  if (user.usage > this.config.portrait_usage - 1)
@@ -1647,9 +1703,9 @@ ${this.config.mood_prompt_2}
1647
1703
  };
1648
1704
 
1649
1705
  // src/game.ts
1650
- var import_koishi12 = require("koishi");
1706
+ var import_koishi11 = require("koishi");
1651
1707
 
1652
- // src/gamegobang.ts
1708
+ // src/gamefencing.ts
1653
1709
  var import_koishi9 = require("koishi");
1654
1710
 
1655
1711
  // src/abstractGame.ts
@@ -1753,270 +1809,8 @@ var abstractGame = class {
1753
1809
  }
1754
1810
  };
1755
1811
 
1756
- // src/gamegobang.ts
1757
- var import_child_process = require("child_process");
1758
- var path6 = __toESM(require("path"));
1759
- var logger8 = new import_koishi9.Logger("satori-game-gobang");
1760
- var BOARD_SIZE = 14;
1761
- var inspireSearchLength = 10;
1762
- var Coordinate = class {
1763
- static {
1764
- __name(this, "Coordinate");
1765
- }
1766
- x;
1767
- y;
1768
- score;
1769
- constructor(a = 0, b = 0, s = 0) {
1770
- this.x = a;
1771
- this.y = b;
1772
- this.score = s;
1773
- }
1774
- };
1775
- async function wrapInHTML(str) {
1776
- if (!puppeteer) {
1777
- logger8.warn("puppeteer未就绪");
1778
- return "出现错误,请联系管理员";
1779
- }
1780
- return puppeteer.render(`<html><body>${str.replaceAll(/\n/g, "<BR>")}</body>
1781
- <style>
1782
- body {
1783
- padding: 10px;
1784
- display: inline-block;
1785
- }
1786
- </style>
1787
- </html>`);
1788
- }
1789
- __name(wrapInHTML, "wrapInHTML");
1790
- var goBangSingleGame = class extends abstractGameSingleGame {
1791
- static {
1792
- __name(this, "goBangSingleGame");
1793
- }
1794
- playerFlag;
1795
- // 玩家棋子颜色(1: 黑棋,2: 白棋)
1796
- winningFlag = 4 /* pending */;
1797
- // 当前胜负状态
1798
- level;
1799
- // AI 难度等级
1800
- board = [];
1801
- // BOARD_SIZExBOARD_SIZE 棋盘状态
1802
- lastScore = 0;
1803
- // 上次评分
1804
- turnsCount = 0;
1805
- // 回合数
1806
- constructor(disposeListener, session) {
1807
- super(disposeListener, session);
1808
- }
1809
- // 初始化棋盘,随机决定玩家先手
1810
- startGame = () => {
1811
- super.startGame();
1812
- this.board = Array.from({ length: BOARD_SIZE }, () => Array(BOARD_SIZE).fill(0));
1813
- this.turnsCount = 0;
1814
- this.playerFlag = Math.round(Math.random()) + 1;
1815
- const getCornerCoordinate = /* @__PURE__ */ __name((i) => {
1816
- const quarter = i < 0.5 ? 0.2 : 0.7;
1817
- const base = Math.round(BOARD_SIZE * quarter);
1818
- return base;
1819
- }, "getCornerCoordinate");
1820
- for (let i = 0; i <= 1; i++) {
1821
- for (let j = 0; j <= 1; j++) {
1822
- const randomX = getCornerCoordinate(i) + 1 - 2 * i;
1823
- const randomY = getCornerCoordinate(j);
1824
- const player = Math.round(Math.random()) + 1;
1825
- this.board[randomX][randomY] = player;
1826
- this.board[randomX + i * 2 - 1][randomY - j * 2 + 1] = 3 - player;
1827
- }
1828
- }
1829
- if (this.playerFlag === 1) {
1830
- this.board[BOARD_SIZE / 2][BOARD_SIZE / 2] = 1;
1831
- this.board[BOARD_SIZE / 2 + (Math.round(Math.random()) ? -1 : 1)][BOARD_SIZE / 2 + (Math.round(Math.random()) ? -1 : 1)] = 2;
1832
- return wrapInHTML("游戏开始,你随机到了先手(黑)\n输入两个数字以下棋,先行后列,例如:“0 13”\n" + this.printBoard());
1833
- } else {
1834
- this.board[BOARD_SIZE / 2][BOARD_SIZE / 2] = 1;
1835
- this.board[BOARD_SIZE / 2 + (Math.round(Math.random()) ? -1 : 1)][BOARD_SIZE / 2 + (Math.round(Math.random()) ? -1 : 1)] = 2;
1836
- this.board[BOARD_SIZE / 2][BOARD_SIZE / 2 + (Math.round(Math.random()) ? -1 : 1)] = 1;
1837
- return wrapInHTML("游戏开始,你随机到了后手(白)\n输入两个数字以下棋,先行后列,例如:“0 13”\n" + this.printBoard());
1838
- }
1839
- };
1840
- // 结束游戏,返回结果
1841
- endGame = async () => {
1842
- super.endGame();
1843
- let bonusLevel = this.level;
1844
- if (this.winningFlag === 1 /* win */ && this.playerFlag === 1) {
1845
- bonusLevel -= 1;
1846
- } else if (this.winningFlag === 2 /* lose */ && this.playerFlag === 1) {
1847
- bonusLevel += 1;
1848
- }
1849
- return { message: `${bonusLevel}`, win: this.winningFlag, gameName: "五子棋", playerID: this.session.userId };
1850
- };
1851
- /**
1852
- * 处理玩家输入(落子坐标)
1853
- * @param str 输入内容,格式为 "x y"
1854
- */
1855
- async processInput(str) {
1856
- const [x, y] = str.split(" ").map((a) => {
1857
- return parseInt(a);
1858
- });
1859
- if (isNaN(x) || isNaN(y))
1860
- return;
1861
- if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
1862
- return "坐标超出范围了";
1863
- if (this.board[x][y] !== 0)
1864
- return "这个位置已经有棋子了,别想耍赖";
1865
- if (this.winningFlag !== 4 /* pending */)
1866
- return "游戏已结束";
1867
- this.lastActionTime = Date.now();
1868
- this.board[x][y] = this.playerFlag;
1869
- logger8.info(`level: ${this.level}, player: x: ${x}, y: ${y}`);
1870
- if (this.checkWin(x, y))
1871
- return wrapInHTML(this.printBoard() + "\n游戏已结束,发送结束游戏退出");
1872
- const starttime = Date.now();
1873
- const aiMove = this.getAIMove();
1874
- const endtime = Date.now();
1875
- if (aiMove.x === -1 || aiMove.y === -1) {
1876
- this.winningFlag = 3 /* draw */;
1877
- return "计算失败,视为平局,发送结束游戏退出";
1878
- }
1879
- if (this.turnsCount > 50) {
1880
- this.winningFlag = 3 /* draw */;
1881
- return "平局,发送结束游戏退出";
1882
- }
1883
- logger8.info(`AI 落子坐标: ${aiMove.x} ${aiMove.y},得分: ${aiMove.score},AI 落子耗时: ${endtime - starttime}ms`);
1884
- this.board[aiMove.x][aiMove.y] = 3 - this.playerFlag;
1885
- if (this.turnsCount > 5) {
1886
- setTimeout(() => {
1887
- this.session.send(this.generateChat(aiMove.score));
1888
- }, 1e3);
1889
- }
1890
- this.lastScore = aiMove.score;
1891
- this.turnsCount++;
1892
- if (this.checkWin(aiMove.x, aiMove.y))
1893
- return wrapInHTML(this.printBoard() + "\n游戏已结束,发送结束游戏退出");
1894
- return wrapInHTML(this.printBoard() + "\n我这一步下在这里哦(" + aiMove.x + " " + aiMove.y + " 用时" + (endtime - starttime) + "ms)");
1895
- }
1896
- getAIMove() {
1897
- try {
1898
- const boardStr = this.board.map((row) => row.join(" ")).join(" ");
1899
- const exePath = path6.resolve(__dirname, "../lib/gobang_ai.exe");
1900
- const level = this.turnsCount > 3 ? this.level : Math.min(5, this.level);
1901
- const stdout = (0, import_child_process.execSync)(
1902
- `"${exePath}" "${boardStr}" ${this.playerFlag} ${level} ${inspireSearchLength}`,
1903
- { timeout: 18e5 }
1904
- // 设置1800秒超时
1905
- ).toString().trim();
1906
- const [x, y, score] = stdout.split(" ").map(Number);
1907
- return new Coordinate(x, y, score);
1908
- } catch (error) {
1909
- logger8.error(`AI计算失败: ${error}`);
1910
- }
1911
- return new Coordinate(-1, -1, -1);
1912
- }
1913
- // 检查是否连成五子
1914
- checkWin(x, y) {
1915
- const directions = [[1, 0], [0, 1], [1, 1], [1, -1]];
1916
- for (const [dx, dy] of directions) {
1917
- let count = 1;
1918
- for (let i = 1; i < 5; i++) {
1919
- if (this.checkDirection(x, y, dx, dy, i))
1920
- count++;
1921
- else
1922
- break;
1923
- }
1924
- for (let i = 1; i < 5; i++) {
1925
- if (this.checkDirection(x, y, -dx, -dy, i))
1926
- count++;
1927
- else
1928
- break;
1929
- }
1930
- if (count >= 5) {
1931
- this.winningFlag = this.board[x][y] === this.playerFlag ? 1 /* win */ : 2 /* lose */;
1932
- return true;
1933
- }
1934
- }
1935
- return false;
1936
- }
1937
- // 辅助方法:检查指定方向是否有连续棋子
1938
- checkDirection(x, y, dx, dy, step) {
1939
- const nx = x + dx * step, ny = y + dy * step;
1940
- return nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && this.board[nx][ny] === this.board[x][y];
1941
- }
1942
- // 生成带表情符号的棋盘字符串
1943
- printBoard() {
1944
- const numberEmojis = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟", "🔢", "🔢", "🔢", "🔢"];
1945
- let res = "🟨" + numberEmojis.slice(0, BOARD_SIZE).join("") + "🟨\n";
1946
- for (let i = 0; i < BOARD_SIZE; i++) {
1947
- res += numberEmojis[i];
1948
- for (let j = 0; j < BOARD_SIZE; j++) {
1949
- res += this.board[i][j] === 0 ? "🟨" : this.board[i][j] === 1 ? "⚫" : "⚪";
1950
- }
1951
- res += numberEmojis[i];
1952
- res += "\n";
1953
- }
1954
- res += "🟨" + numberEmojis.slice(0, BOARD_SIZE).join("") + "🟨\n";
1955
- return res;
1956
- }
1957
- generateChat(Score) {
1958
- if (this.lastScore < 5e6 && Score > 5e6 && this.level > 3) {
1959
- return "我觉得你要输了哦~";
1960
- }
1961
- if (this.lastScore < 1e6 && Score > 1e6) {
1962
- return "只能下在这里了~";
1963
- }
1964
- if (4e3 <= Score && Score < 1e4) {
1965
- return "这招怎么样?";
1966
- }
1967
- if (2e3 <= Score && Score < 4e3 && Math.random() < 0.5) {
1968
- return {
1969
- 0: "有点意思哦~",
1970
- 1: "真是焦灼的局面",
1971
- 2: "势均力敌呢~",
1972
- 3: "你在想什么呢?",
1973
- 4: "走这里会不会太冒险了?"
1974
- }[Math.floor(Math.random() * 5)];
1975
- }
1976
- if (Score < -1e6) {
1977
- return "好猛烈的攻击啊~";
1978
- }
1979
- if (Score < 0 && Math.random() < 0.5) {
1980
- return "你还真是厉害呢~";
1981
- }
1982
- if (Score < 100) {
1983
- return "出乎意料的一手啊";
1984
- }
1985
- }
1986
- };
1987
- var goBang = class extends abstractGame {
1988
- static {
1989
- __name(this, "goBang");
1990
- }
1991
- constructor() {
1992
- super(goBangSingleGame);
1993
- }
1994
- // 启动游戏时可传入难度参数
1995
- async startGame(session, ctx, args) {
1996
- let level;
1997
- if (!isNaN(parseInt(args[0])))
1998
- level = parseInt(args[0]);
1999
- else {
2000
- setTimeout(() => {
2001
- session.send("未输入难度等级(2-6),默认设为3");
2002
- }, 500);
2003
- level = 3;
2004
- }
2005
- if (level < 2 || level > 6) {
2006
- level = level < 2 ? 2 : 6;
2007
- setTimeout(() => {
2008
- session.send("难度等级必须在2到6之间,已调整为" + level);
2009
- }, 500);
2010
- }
2011
- const game = await super.startGame(session, ctx, args);
2012
- game.level = level;
2013
- return game;
2014
- }
2015
- };
2016
-
2017
1812
  // src/gamefencing.ts
2018
- var import_koishi10 = require("koishi");
2019
- var logger9 = new import_koishi10.Logger("satori-game-fencing");
1813
+ var logger8 = new import_koishi9.Logger("satori-game-fencing");
2020
1814
  var fencingSingleGame = class _fencingSingleGame extends abstractGameSingleGame {
2021
1815
  static {
2022
1816
  __name(this, "fencingSingleGame");
@@ -2198,8 +1992,8 @@ var fencing = class extends abstractGame {
2198
1992
  };
2199
1993
 
2200
1994
  // src/gameOneTouch.ts
2201
- var import_koishi11 = require("koishi");
2202
- var logger10 = new import_koishi11.Logger("satori-game-onetouch");
1995
+ var import_koishi10 = require("koishi");
1996
+ var logger9 = new import_koishi10.Logger("satori-game-onetouch");
2203
1997
  var SKILL_MAP = {
2204
1998
  // 基础技能
2205
1999
  "-1": {},
@@ -2290,7 +2084,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2290
2084
  hp: this.baseHP,
2291
2085
  status: 0 /* Normal */
2292
2086
  };
2293
- return await wrapInHTML2(`游戏开始!
2087
+ return await wrapInHTML(`游戏开始!
2294
2088
  对方的手势:左${this.ai.left} 右${this.ai.right}
2295
2089
  你的手势:左${this.player.left} 右${this.player.right}
2296
2090
  输入格式"左 右"选择要触碰的手
@@ -2340,9 +2134,9 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2340
2134
  }
2341
2135
  async processInput(input) {
2342
2136
  if (input === "游戏规则")
2343
- return await wrapInHTML2(this.instuction);
2137
+ return await wrapInHTML(this.instuction);
2344
2138
  if (input === "技能列表")
2345
- return await wrapInHTML2(this.skillList);
2139
+ return await wrapInHTML(this.skillList);
2346
2140
  if (this.turnCount === 0)
2347
2141
  this.initState(this.level);
2348
2142
  this.turnCount++;
@@ -2383,7 +2177,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2383
2177
  statusIcon(this.player.vulnerablility, "💔"),
2384
2178
  this.player.status === 1 /* Stunned */ ? "💫 眩晕" : ""
2385
2179
  ].filter(Boolean).join(" | ");
2386
- return wrapInHTML2(`▶️你的行动:${result}
2180
+ return wrapInHTML(`▶️你的行动:${result}
2387
2181
 
2388
2182
  ▶️我的行动:${aiResult}
2389
2183
 
@@ -2768,7 +2562,7 @@ ${bonusMessage}` : "");
2768
2562
  bestMove = move;
2769
2563
  }
2770
2564
  }
2771
- logger10.info(`AI选择的动作:${bestMove[0]}碰${bestMove[1]}, 分数:${bestScore}`);
2565
+ logger9.info(`AI选择的动作:${bestMove[0]}碰${bestMove[1]}, 分数:${bestScore}`);
2772
2566
  if (this.turnCount > 5)
2773
2567
  this.session.sendQueued(this.generateChat(bestScore), 1e3);
2774
2568
  this.lastScore = bestScore;
@@ -2994,9 +2788,9 @@ var OneTouchGame = class extends abstractGame {
2994
2788
  return game;
2995
2789
  }
2996
2790
  };
2997
- async function wrapInHTML2(str) {
2791
+ async function wrapInHTML(str) {
2998
2792
  if (!puppeteer) {
2999
- logger10.warn("puppeteer未就绪");
2793
+ logger9.warn("puppeteer未就绪");
3000
2794
  return "出现错误,请联系管理员";
3001
2795
  }
3002
2796
  return puppeteer.render(`<html><body>${str.replaceAll(/\n/g, "<BR>")}</body>
@@ -3008,15 +2802,15 @@ async function wrapInHTML2(str) {
3008
2802
  </style>
3009
2803
  </html>`);
3010
2804
  }
3011
- __name(wrapInHTML2, "wrapInHTML");
2805
+ __name(wrapInHTML, "wrapInHTML");
3012
2806
 
3013
2807
  // src/game.ts
3014
- var logger11 = new import_koishi12.Logger("satori-game");
2808
+ var logger10 = new import_koishi11.Logger("satori-game");
3015
2809
  var Game = class {
3016
2810
  static {
3017
2811
  __name(this, "Game");
3018
2812
  }
3019
- GAMES = ["五子棋", "击剑", "一碰一"];
2813
+ GAMES = ["一碰一"];
3020
2814
  // 支持的游戏列表
3021
2815
  channelGames = /* @__PURE__ */ new Map();
3022
2816
  // 频道ID到游戏名称的映射
@@ -3030,9 +2824,6 @@ var Game = class {
3030
2824
  this.context = ctx;
3031
2825
  this.config = cfg;
3032
2826
  this.sat = sat;
3033
- if (this.config.enable_gobang) {
3034
- this.availableGames.set("五子棋", new goBang());
3035
- }
3036
2827
  if (this.config.enable_fencing) {
3037
2828
  this.availableGames.set("击剑", new fencing());
3038
2829
  }
@@ -3051,8 +2842,6 @@ var Game = class {
3051
2842
  }
3052
2843
  // 启动指定游戏
3053
2844
  async startGame(session, gameName, args) {
3054
- if (this.config.game_block_channel.includes(session.channelId))
3055
- return "本群不支持游戏哦" + (this.config.channel_id_for_gobang[0] ? ",想玩请加群" + this.config.channel_id_for_gobang[0] : "");
3056
2845
  if (this.channelGames.get(session.channelId))
3057
2846
  return "当前频道已经有游戏在进行中";
3058
2847
  if (gameName == "")
@@ -3062,9 +2851,6 @@ var Game = class {
3062
2851
  const game = this.availableGames.get(gameName);
3063
2852
  if (!game)
3064
2853
  return "没有这个游戏哦";
3065
- if (gameName === "五子棋" && !this.config.channel_id_for_gobang.includes(session.channelId)) {
3066
- return "本群不支持五子棋哦,想玩请加群" + this.config.channel_id_for_gobang[0];
3067
- }
3068
2854
  const user = await ensureUserExists(this.context, session.userId, session.username);
3069
2855
  const lastUsage = this.userUsage.get(`${session.userId}-${gameName}`);
3070
2856
  if (lastUsage && user.userlevel < 4) {
@@ -3079,7 +2865,7 @@ var Game = class {
3079
2865
  refreshPuppeteer(this.context);
3080
2866
  game.startGame(session, this.context, args);
3081
2867
  this.channelGames.set(session.channelId, gameName);
3082
- logger11.info(`游戏${gameName}已开始于${session.channelId}`);
2868
+ logger10.info(`游戏${gameName}已开始于${session.channelId}`);
3083
2869
  }
3084
2870
  // 结束当前游戏
3085
2871
  async endGame(session) {
@@ -3088,7 +2874,7 @@ var Game = class {
3088
2874
  return "当前频道没有游戏在进行中";
3089
2875
  if (await this.availableGames.get(gameName).endGame(session, this.context)) {
3090
2876
  this.channelGames.delete(session.channelId);
3091
- logger11.info(`${session.channelId}的游戏已结束`);
2877
+ logger10.info(`${session.channelId}的游戏已结束`);
3092
2878
  }
3093
2879
  }
3094
2880
  async chat(session, gameName, prompt) {
@@ -3101,8 +2887,6 @@ var Game = class {
3101
2887
  }
3102
2888
  getGameCd(gameName) {
3103
2889
  switch (gameName) {
3104
- case "五子棋":
3105
- return this.config.cd_for_gobang;
3106
2890
  case "一碰一":
3107
2891
  return this.config.cd_for_OneTouch;
3108
2892
  default:
@@ -3114,7 +2898,7 @@ var Game = class {
3114
2898
  this.GAMES.forEach((game) => {
3115
2899
  list += game + "\n";
3116
2900
  });
3117
- logger11.info(list);
2901
+ logger10.info(list);
3118
2902
  return list;
3119
2903
  }
3120
2904
  async resultJudge(result, ctx, session) {
@@ -3160,8 +2944,8 @@ var Game = class {
3160
2944
  };
3161
2945
 
3162
2946
  // src/broadcast.ts
3163
- var import_koishi13 = require("koishi");
3164
- var logger12 = new import_koishi13.Logger("satori-ai-broadcast");
2947
+ var import_koishi12 = require("koishi");
2948
+ var logger11 = new import_koishi12.Logger("satori-ai-broadcast");
3165
2949
  var BroadcastManager = class {
3166
2950
  static {
3167
2951
  __name(this, "BroadcastManager");
@@ -3191,16 +2975,16 @@ var BroadcastManager = class {
3191
2975
  const id = `broadcast-${this.idCounter++}`;
3192
2976
  const broadcast = new broadcastMessage(this.ctx, this.config, message, img);
3193
2977
  this.broadcastMessages.set(id, broadcast);
3194
- logger12.info(`广播已创建 ID: ${id}`);
2978
+ logger11.info(`广播已创建 ID: ${id}`);
3195
2979
  return `广播已创建 ID: ${id}`;
3196
2980
  }
3197
2981
  async deleteBroadcast(id) {
3198
2982
  if (this.broadcastMessages.has(id)) {
3199
2983
  this.broadcastMessages.delete(id);
3200
- logger12.info(`广播已删除 ID: ${id}`);
2984
+ logger11.info(`广播已删除 ID: ${id}`);
3201
2985
  return "已删除广播:" + id;
3202
2986
  } else {
3203
- logger12.warn(`未找到广播 ID: ${id}`);
2987
+ logger11.warn(`未找到广播 ID: ${id}`);
3204
2988
  return "未找到该广播";
3205
2989
  }
3206
2990
  }
@@ -3208,7 +2992,7 @@ var BroadcastManager = class {
3208
2992
  for (const [id, broadcast] of this.broadcastMessages.entries()) {
3209
2993
  if (broadcast.setTime.getTime() + 1e3 * 60 * this.config.waiting_time < Date.now()) {
3210
2994
  this.broadcastMessages.delete(id);
3211
- logger12.info(`广播已过期 ID: ${id}`);
2995
+ logger11.info(`广播已过期 ID: ${id}`);
3212
2996
  continue;
3213
2997
  }
3214
2998
  await broadcast.sendMessage(session);
@@ -3236,7 +3020,7 @@ ${alreadySentChannels}
3236
3020
  }
3237
3021
  }
3238
3022
  const result = list.length > 0 ? list.join("\n") : null;
3239
- return result ? wrapInHTML3(result) : null;
3023
+ return result ? wrapInHTML2(result) : null;
3240
3024
  }
3241
3025
  };
3242
3026
  var broadcastMessage = class {
@@ -3267,13 +3051,13 @@ var broadcastMessage = class {
3267
3051
  try {
3268
3052
  this.alreadySentChannels.add(channelId);
3269
3053
  if (this.img) {
3270
- session.send(await wrapInHTML3(this.message));
3054
+ session.send(await wrapInHTML2(this.message));
3271
3055
  } else {
3272
3056
  session.send(this.message);
3273
3057
  }
3274
- logger12.info(`向群 ${channelId} 广播: ${this.message}`);
3058
+ logger11.info(`向群 ${channelId} 广播: ${this.message}`);
3275
3059
  } catch (error) {
3276
- logger12.error(`广播失败 ${channelId}: ${error}`);
3060
+ logger11.error(`广播失败 ${channelId}: ${error}`);
3277
3061
  }
3278
3062
  }
3279
3063
  getAlreadySentChannels() {
@@ -3281,9 +3065,9 @@ var broadcastMessage = class {
3281
3065
  return alreadySentChannels.length > 0 ? alreadySentChannels.join("\n") : "无";
3282
3066
  }
3283
3067
  };
3284
- async function wrapInHTML3(str) {
3068
+ async function wrapInHTML2(str) {
3285
3069
  if (!puppeteer) {
3286
- logger12.warn("puppeteer未就绪");
3070
+ logger11.warn("puppeteer未就绪");
3287
3071
  return str;
3288
3072
  }
3289
3073
  return puppeteer.render(`<html><body>${str.replaceAll(/\n/g, "<BR>")}</body>
@@ -3295,16 +3079,17 @@ async function wrapInHTML3(str) {
3295
3079
  </style>
3296
3080
  </html>`);
3297
3081
  }
3298
- __name(wrapInHTML3, "wrapInHTML");
3082
+ __name(wrapInHTML2, "wrapInHTML");
3299
3083
 
3300
3084
  // src/index.tsx
3301
- var logger13 = new import_koishi14.Logger("satori-ai");
3085
+ var logger12 = new import_koishi13.Logger("satori-ai");
3086
+ var randomPrompt = "根据群聊内最近的包括所有人的聊天记录,锐评一下群聊中的话题";
3302
3087
  var puppeteer = null;
3303
3088
  function refreshPuppeteer(ctx) {
3304
3089
  if (ctx.puppeteer) {
3305
3090
  puppeteer = ctx.puppeteer;
3306
3091
  } else {
3307
- logger13.warn("puppeteer未就绪");
3092
+ logger12.warn("puppeteer未就绪");
3308
3093
  }
3309
3094
  }
3310
3095
  __name(refreshPuppeteer, "refreshPuppeteer");
@@ -3433,7 +3218,8 @@ var SAT = class extends Sat {
3433
3218
  registerCommands(ctx) {
3434
3219
  ctx.command("sat <text:text>", { authority: this.config.authority }).alias(...this.config.alias).action(async ({ session }, prompt) => this.handleSatCommand(session, prompt));
3435
3220
  ctx.command("sat.clear", "清空会话").option("global", "-g").action(({ session, options }) => this.clearSession(session, options.global));
3436
- ctx.command("sat.common_sense <text:text>", "添加常识").action(async ({ session }, prompt) => this.addCommonSense(session, prompt));
3221
+ ctx.command("sat.common_sense <text:text>", "添加常识", { authority: 4 }).alias("添加常识").action(async ({ session }, prompt) => this.addCommonSense(session, prompt));
3222
+ ctx.command("sat.group_sense <text:text>", "添加群常识", { authority: 3 }).alias("添加群常识").option("id", "-i <id:string>", { authority: 3 }).action(async ({ session, options }, prompt) => this.addGroupCommonSense(session, prompt, options.id));
3437
3223
  ctx.command("sat.update_user_level", "更新用户等级", { authority: 2 }).alias("更新用户").option("id", "-i <id:string>", { authority: 4 }).option("level", "-l <level:number>", { authority: 4 }).action(async ({ session, options }) => this.handleUserLevel(session, options));
3438
3224
  ctx.command("sat.user_usage", "查看用户使用次数").alias("查询次数").action(async ({ session }) => this.handleUserUsage(session));
3439
3225
  ctx.command("sat.add_output_censor <text:text>", "添加输出敏感词", { authority: 4 }).alias("添加输出屏蔽词").action(async ({ session }, word) => addOutputCensor(session, word, this.config.dataDir));
@@ -3492,9 +3278,9 @@ var SAT = class extends Sat {
3492
3278
  const messages = generateAuxiliaryPrompt(prompt, response.content, user, this.getFavorabilityConfig());
3493
3279
  const result = await this.apiClient.auxiliaryChat(messages);
3494
3280
  if (result.error) {
3495
- logger13.error(`辅助判断失败:${result.content}`);
3281
+ logger12.error(`辅助判断失败:${result.content}`);
3496
3282
  } else {
3497
- logger13.info(`辅助判断:${result.content}`);
3283
+ logger12.info(`辅助判断:${result.content}`);
3498
3284
  return handleAuxiliaryResult(this.ctx, session, this.getFavorabilityConfig(), result.content);
3499
3285
  }
3500
3286
  }
@@ -3581,81 +3367,111 @@ var SAT = class extends Sat {
3581
3367
  await inputContentCheck(this.ctx, censored, session.userId, this.getFavorabilityConfig(), session, this.moodManager);
3582
3368
  }
3583
3369
  if (this.config.log_ask_response)
3584
- logger13.info(`用户 ${session.username}:${processedPrompt}`);
3370
+ logger12.info(`用户 ${session.username}:${processedPrompt}`);
3585
3371
  return censored;
3586
3372
  }
3587
3373
  // 生成回复
3588
3374
  async generateResponse(session, prompt) {
3589
3375
  if (this.getChannelParallelCount(session) > this.config.max_parallel_count) {
3590
- logger13.info(`频道 ${session.channelId} 并发数过高(${this.getChannelParallelCount(session)}),${session.username}等待中...`);
3376
+ logger12.info(`频道 ${session.channelId} 并发数过高(${this.getChannelParallelCount(session)}),${session.username}等待中...`);
3591
3377
  }
3592
3378
  while (this.getChannelParallelCount(session) > this.config.max_parallel_count) {
3593
3379
  this.updateChannelParallelCount(session, -1);
3594
- await new Promise((resolve3) => setTimeout(resolve3, 1e3));
3380
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
3595
3381
  this.updateChannelParallelCount(session, 1);
3596
3382
  }
3597
3383
  const messages = await this.buildMessages(session, prompt);
3598
- logger13.info(`频道 ${session.channelId} 处理:${session.userId},剩余${this.getChannelParallelCount(session)}并发`);
3384
+ logger12.info(`频道 ${session.channelId} 处理:${session.userId},剩余${this.getChannelParallelCount(session)}并发`);
3599
3385
  const user = await getUser(this.ctx, session.userId);
3600
- let response = await this.getChatResponse(user, messages);
3386
+ let response = await this.getChatResponse(user, messages, prompt);
3601
3387
  if (response.error)
3602
- response = await this.getChatResponse(user, messages);
3388
+ response = await this.getChatResponse(user, messages, prompt);
3603
3389
  if (response.error)
3604
3390
  updateUserUsage(this.ctx, user, -1);
3605
3391
  return response;
3606
3392
  }
3607
3393
  // 获取聊天回复
3608
- async getChatResponse(user, messages) {
3394
+ async getChatResponse(user, messages, prompt) {
3395
+ const hasTicket = user?.items?.["地灵殿通行证"]?.description && user.items["地灵殿通行证"].description === "on";
3396
+ const maxLength = hasTicket ? user?.items?.["地灵殿通行证"]?.metadata?.use_not_reasoner_LLM_length : this.config.use_not_reasoner_LLM_length;
3397
+ const useNoReasoner = prompt.length <= maxLength && this.config.enable_reasoner_like;
3609
3398
  let response = await this.apiClient.chat(user, messages);
3610
3399
  if (this.config.log_ask_response) {
3611
3400
  if (this.config.enable_favorability && this.config.enable_mood)
3612
- logger13.info(`Satori AI:(心情值:${this.moodManager.getMoodValue(user.userid)})${response.content}`);
3401
+ logger12.info(`Satori AI:(心情值:${this.moodManager.getMoodValue(user.userid)})${response.content}`);
3613
3402
  else
3614
- logger13.info(`Satori AI:${response.content}`);
3403
+ logger12.info(`Satori AI:${response.content}`);
3404
+ }
3405
+ if (!response.error) {
3406
+ response = filterResponse(response.content, this.config.reasoner_filter_word.split("-"), {
3407
+ applyBracketFilter: this.config.reasoner_filter,
3408
+ applyTagFilter: useNoReasoner || this.config.enhanceReasoningProtection
3409
+ });
3615
3410
  }
3616
- if (this.config.reasoner_filter && !response.error)
3617
- response = filterResponse(response.content, this.config.reasoner_filter_word.split("-"));
3618
3411
  return response;
3619
3412
  }
3620
3413
  // 构建消息
3621
3414
  async buildMessages(session, prompt) {
3622
3415
  const messages = [];
3623
- const example = `<think>
3416
+ const user = await ensureUserExists(this.ctx, session.userId, session.username);
3417
+ const hasTicket = user?.items?.["地灵殿通行证"]?.description && user.items["地灵殿通行证"].description === "on";
3418
+ const maxLength = hasTicket ? user?.items?.["地灵殿通行证"]?.metadata?.use_not_reasoner_LLM_length : this.config.use_not_reasoner_LLM_length;
3419
+ const useNoReasoner = prompt.length <= maxLength && this.config.enable_reasoner_like;
3420
+ const exampleWithoutReasoner = `<think>
3624
3421
  好的,我会尽量做到的
3625
3422
  </think>
3626
3423
  <p>已明确对话要求</p>`;
3424
+ const exampleWithTag = `<p>已明确对话要求</p>`;
3425
+ const exampleWithoutTag = `已明确对话要求`;
3426
+ const example = useNoReasoner ? exampleWithoutReasoner : this.config.enhanceReasoningProtection ? exampleWithTag : exampleWithoutTag;
3627
3427
  if (this.config.no_system_prompt) {
3628
3428
  messages.push({ role: "user", content: await this.buildSystemPrompt(session, prompt) });
3629
3429
  messages.push({ role: "assistant", content: example });
3630
3430
  } else {
3631
3431
  messages.push({ role: "system", content: await this.buildSystemPrompt(session, prompt) });
3632
3432
  }
3633
- const userMemory = this.memoryManager.getChannelContext(this.config.personal_memory ? session.userId : session.channelId);
3634
- messages.push(...userMemory);
3433
+ if (prompt != randomPrompt) {
3434
+ const userMemory = this.memoryManager.getChannelContext(this.config.personal_memory ? session.userId : session.channelId);
3435
+ messages.push(...userMemory);
3436
+ }
3635
3437
  messages.push({ role: "user", content: prompt });
3636
3438
  let payload = messages.map((msg) => msg.role + ":" + msg.content).join("\n");
3637
3439
  if (this.config.log_system_prompt)
3638
- logger13.info(`系统提示:
3440
+ logger12.info(`系统提示:
3639
3441
  ${payload}`);
3640
3442
  return messages;
3641
3443
  }
3642
3444
  // 构建系统提示
3643
3445
  async buildSystemPrompt(session, prompt) {
3644
3446
  const commonSense = await this.memoryManager.searchMemories(session, prompt, "common");
3447
+ const groupSense = await this.memoryManager.searchMemories(session, prompt, "group");
3645
3448
  const channelDialogue = await this.memoryManager.getChannelDialogue(session);
3646
3449
  const userMemory = await this.memoryManager.searchMemories(session, prompt);
3647
3450
  const user = await getUser(this.ctx, session.userId);
3648
3451
  const moodLevel = this.moodManager.getMoodLevel(user.userid);
3649
3452
  let systemPrompt = "";
3453
+ if (prompt == randomPrompt) {
3454
+ systemPrompt += "#首先明确一些参考信息\n";
3455
+ systemPrompt += "\n##" + channelDialogue;
3456
+ systemPrompt += this.getThinkingPrompt(user, prompt);
3457
+ systemPrompt += this.config.prompt;
3458
+ if (this.config.enhanceReasoningProtection) {
3459
+ systemPrompt += `
3460
+ #注意:你最终的回复内容必须使用“<p>”开头,使用“</p>”结尾
3461
+ `;
3462
+ if (this.config.no_system_prompt)
3463
+ systemPrompt += "\n#如果你明白以上内容,请回复“<p>已明确对话要求</p>”";
3464
+ }
3465
+ return systemPrompt;
3466
+ }
3650
3467
  systemPrompt += "#首先明确一些参考信息\n";
3651
3468
  systemPrompt += "\n##" + commonSense;
3469
+ systemPrompt += "\n##" + groupSense;
3652
3470
  systemPrompt += "\n##" + channelDialogue;
3653
3471
  systemPrompt += "\n##" + userMemory;
3654
3472
  if (moodLevel == "normal" || moodLevel == "happy")
3655
3473
  systemPrompt += "\n##" + this.portraitManager.getUserPortrait(session);
3656
- systemPrompt += "\n#参考信息到此为止,接下来是思考要求\n";
3657
3474
  systemPrompt += this.getThinkingPrompt(user, prompt);
3658
- systemPrompt += "\n#接下来是对话要求,以下要求仅对最终的回复内容生效,不限制思考过程\n";
3659
3475
  systemPrompt += this.config.prompt;
3660
3476
  if (user?.items?.["觉的衣柜"]?.count && (moodLevel == "normal" || moodLevel == "happy")) {
3661
3477
  const clothes = user?.items?.["觉的衣柜"]?.metadata?.clothes;
@@ -3683,23 +3499,31 @@ ${payload}`);
3683
3499
  if (nickName)
3684
3500
  systemPrompt += `, 昵称是:${nickName},称呼用户时请优先使用昵称
3685
3501
  `;
3686
- systemPrompt += `
3502
+ if (this.config.enhanceReasoningProtection) {
3503
+ systemPrompt += `
3687
3504
  #注意:你最终的回复内容必须使用“<p>”开头,使用“</p>”结尾
3688
3505
  `;
3689
- if (this.config.no_system_prompt)
3690
- systemPrompt += "\n#如果你明白以上内容,请回复“<p>已明确对话要求</p>”";
3506
+ if (this.config.no_system_prompt)
3507
+ systemPrompt += "\n#如果你明白以上内容,请回复“<p>已明确对话要求</p>”";
3508
+ }
3691
3509
  return systemPrompt;
3692
3510
  }
3693
3511
  // 思考提示
3694
3512
  getThinkingPrompt(user, prompt) {
3695
3513
  const reasonerPrompt = this.config.reasoner_prompt;
3696
- const promptForNoReasoner = `#请你在回复时先进行分析思考,并且模仿思维链的模式输出思考内容,${reasonerPrompt};
3514
+ const promptForNoReasoner = `
3515
+ #参考信息到此为止,接下来是思考要求
3516
+ #请你在回复时先进行分析思考,并且模仿思维链的模式输出思考内容,${reasonerPrompt};
3697
3517
  #你在思考时必须以 "<think>" 开头, "</think>" 结尾。仔细揣摩用户意图,**完整输出思考内容后**再输出正式的回复内容;
3698
- #注意:你的正式回复内容必须使用“<p>”开头,使用“</p>”结尾,并且无论如何都要把标签输出完整
3699
- `;
3700
- const promptForReasoner = `#你在思考时必须以 "嗯" 开头。仔细揣摩用户意图,思考结束后返回符合要求的回复。
3701
- #注意:你的回复内容必须使用“<p>”开头,使用“</p>”结尾
3518
+ #注意:你的正式回复内容必须使用“<p>”开头,在输出全部回复后使用“</p>”结尾(即输出且仅输出一对标签),并且无论如何都要把标签输出完整
3519
+ #接下来是对话要求,以下要求仅对最终的回复内容生效,不限制思考过程
3702
3520
  `;
3521
+ const promptForReasoner = this.config.enhanceReasoningProtection ? `
3522
+ #参考信息到此为止,接下来是思考要求
3523
+ #你在思考时必须以 "嗯" 开头。仔细揣摩用户意图,思考结束后返回符合要求的回复。
3524
+ #注意:你的回复内容必须使用“<p>”开头,在输出全部回复后使用“</p>”结尾(即输出且仅输出一对标签)
3525
+ #接下来是对话要求,以下要求仅对最终的回复内容生效,不限制思考过程
3526
+ ` : "";
3703
3527
  const hasTicket = user?.items?.["地灵殿通行证"]?.description && user.items["地灵殿通行证"].description === "on";
3704
3528
  const maxLength = hasTicket ? user?.items?.["地灵殿通行证"]?.metadata?.use_not_reasoner_LLM_length : this.config.use_not_reasoner_LLM_length;
3705
3529
  const useNoReasoner = prompt.length <= maxLength;
@@ -3740,10 +3564,10 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3740
3564
  response = `@${session.username} ` + response;
3741
3565
  }
3742
3566
  if (this.config.sentences_divide && response.length > 10) {
3743
- const sentences = splitSentences(response, this.config.min_sentences_length, this.config.max_sentences_length).map((text) => import_koishi14.h.text(text));
3567
+ const sentences = splitSentences(response, this.config.min_sentences_length, this.config.max_sentences_length).map((text) => import_koishi13.h.text(text));
3744
3568
  for (const sentence of sentences) {
3745
3569
  await session.send(sentence);
3746
- await new Promise((resolve3) => setTimeout(resolve3, this.config.time_interval));
3570
+ await new Promise((resolve2) => setTimeout(resolve2, this.config.time_interval));
3747
3571
  }
3748
3572
  return null;
3749
3573
  }
@@ -3753,7 +3577,7 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3753
3577
  if (!this.config.enable_warning)
3754
3578
  return;
3755
3579
  if (auxiliaryResult.includes("好感↓")) {
3756
- logger13.info(`在群${session.channelId}中,${session.username}骚扰我!`);
3580
+ logger12.info(`在群${session.channelId}中,${session.username}骚扰我!`);
3757
3581
  this.usersToWarn.set(session.channelId, session.username);
3758
3582
  }
3759
3583
  }
@@ -3793,13 +3617,25 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3793
3617
  async addCommonSense(session, content) {
3794
3618
  if (!content)
3795
3619
  return session.text("commands.sat.common_sense.messages.no-prompt");
3796
- const filePath = path7.join(this.config.dataDir, "common_sense.txt");
3620
+ const filePath = path6.join(this.config.dataDir, "common_sense.txt");
3797
3621
  await this.memoryManager.saveLongTermMemory(session, [{
3798
3622
  role: "user",
3799
3623
  content: content.replace(/\"/g, "'").trim()
3800
3624
  }], filePath);
3801
3625
  return session.text("commands.sat.common_sense.messages.succeed", [content]);
3802
3626
  }
3627
+ // 添加群常识
3628
+ async addGroupCommonSense(session, content, groupId) {
3629
+ if (!content)
3630
+ return session.text("commands.sat.common_sense.messages.no-prompt");
3631
+ const id = groupId || session.channelId;
3632
+ const filePath = path6.join(this.config.dataDir, `group_sense`, `${id}.txt`);
3633
+ await this.memoryManager.saveLongTermMemory(session, [{
3634
+ role: "user",
3635
+ content: content.replace(/\"/g, "'").trim()
3636
+ }], filePath);
3637
+ return session.text("commands.sat.common_sense.messages.succeed", [`群${id}中:` + content]);
3638
+ }
3803
3639
  // 更新用户等级
3804
3640
  async handleUserLevel(session, options) {
3805
3641
  const userId = options.id || session.userId;
@@ -3828,21 +3664,10 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3828
3664
  return result;
3829
3665
  }
3830
3666
  // 随机中间件转接
3831
- async handleRandomMiddleware(session, prompt) {
3832
- const user = await getUser(this.ctx, session.userId);
3833
- const processedPrompt = processPrompt(session.content);
3834
- if (this.performPreChecks(session, processedPrompt))
3835
- return null;
3836
- if (await this.checkUserDialogueCount(session, user, 0))
3837
- return null;
3838
- if (await this.checkFavorabilityBlock(session))
3839
- return null;
3840
- let censored = processedPrompt;
3841
- if (this.ctx.censor)
3842
- censored = await this.ctx.censor.transform(processedPrompt, session);
3843
- if (censored.includes("**"))
3844
- return null;
3845
- return this.handleSatCommand(session, prompt);
3667
+ async handleRandomMiddleware(session) {
3668
+ await this.updateChannelParallelCount(session, 1);
3669
+ const response = await this.generateResponse(session, randomPrompt);
3670
+ return await this.formatResponse(session, response.content, null);
3846
3671
  }
3847
3672
  // 昵称中间件转接
3848
3673
  async handleNickNameMiddleware(session, prompt) {
package/lib/memory.d.ts CHANGED
@@ -23,7 +23,7 @@ export declare class MemoryManager {
23
23
  clearAllMemories(): void;
24
24
  getChannelMemory(channelId: string): MemoryEntry[];
25
25
  saveLongTermMemory(session: Session, dialogues: MemoryEntry[], filePath?: string): Promise<void>;
26
- searchMemories(session: Session, prompt: string, type?: 'user' | 'common'): Promise<string>;
26
+ searchMemories(session: Session, prompt: string, type?: 'user' | 'common' | 'group'): Promise<string>;
27
27
  private findBestMatches;
28
28
  private calculateMatchScore;
29
29
  private formatMatches;
package/lib/types.d.ts CHANGED
@@ -148,6 +148,7 @@ export declare namespace Sat {
148
148
  baseURL: string;
149
149
  key: string[];
150
150
  appointModel: string;
151
+ enhanceReasoningProtection: boolean;
151
152
  not_reasoner_LLM_URL: string;
152
153
  not_reasoner_LLM: string;
153
154
  not_reasoner_LLM_key: string[];
@@ -191,6 +192,7 @@ export declare namespace Sat {
191
192
  duplicateDialogueCheck: boolean;
192
193
  enable_online_user_check: boolean;
193
194
  random_min_tokens: number;
195
+ enable_random_without_favorability: boolean;
194
196
  randnum: number;
195
197
  sentences_divide: boolean;
196
198
  min_sentences_length: number;
@@ -244,9 +246,6 @@ export declare namespace Sat {
244
246
  waiting_time: number;
245
247
  enable_game: boolean;
246
248
  game_block_channel: string[];
247
- enable_gobang: boolean;
248
- channel_id_for_gobang: string[];
249
- cd_for_gobang: number;
250
249
  enable_fencing: boolean;
251
250
  enable_OneTouch: boolean;
252
251
  cd_for_OneTouch: number;
@@ -9,7 +9,8 @@ export declare class UserPortraitManager {
9
9
  hasPortrait(userId: string): boolean;
10
10
  readPortrait(userId: string): string;
11
11
  private getDialogues;
12
- private buildMessage;
12
+ private buildFirstMessage;
13
+ private buildSecondMessage;
13
14
  generatePortrait(session: Session, user: User, apiClient: APIClient): Promise<void>;
14
15
  private savePortrait;
15
16
  private ensurePortraitFile;
package/lib/utils.d.ts CHANGED
@@ -23,7 +23,10 @@ export declare function processPrompt(prompt: string): string;
23
23
  * @param words 需要过滤的关键词
24
24
  * @returns 处理后的字符串,删除含有关键词的部分
25
25
  */
26
- export declare function filterResponse(prompt: string, words: string[]): {
26
+ export declare function filterResponse(prompt: string, words: string[], options?: {
27
+ applyBracketFilter?: boolean;
28
+ applyTagFilter?: boolean;
29
+ }): {
27
30
  content: string;
28
31
  error: boolean;
29
32
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-satori-ai",
3
3
  "description": "基于 Satori 适配器的智能对话插件,具备用户画像、长期记忆、常识管理和好感度系统",
4
- "version": "1.3.7",
4
+ "version": "1.3.9",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "types": "lib/index.d.ts",
package/lib/gobang_ai.exe DELETED
Binary file