koishi-plugin-satori-ai 1.3.5 → 1.3.7

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.
@@ -41,6 +41,7 @@ declare class OneTouchSingleGame extends abstractGameSingleGame {
41
41
  private evaluateState;
42
42
  private generateChat;
43
43
  private instuction;
44
+ private skillList;
44
45
  }
45
46
  export declare class OneTouchGame extends abstractGame<OneTouchSingleGame> {
46
47
  constructor();
package/lib/index.js CHANGED
@@ -284,11 +284,11 @@ function filterResponse(prompt, words) {
284
284
  const lastAnswer5 = answerMatches5 ? answerMatches5[answerMatches5.length - 1] : null;
285
285
  const answerContent = lastAnswer || lastDoubao || lastAnswer3 || lastAnswer4 || lastAnswer5;
286
286
  if (!answerContent) {
287
- return { content: "有点问题,稍后再来吧", error: true };
287
+ return { content: "有点问题,请重置对话", error: true };
288
288
  }
289
289
  const regex = /<p>|<\/p>|<doubaothinking>|<\/doubaothinking>|<\/p|<answer>|<\/answer>|<br>|<\/br>|<br\/>/g;
290
290
  const cleanedContent = answerContent.replace(regex, "");
291
- return cleanedContent ? { content: cleanedContent, error: false } : { content: "有点问题,稍后再来吧", error: true };
291
+ return cleanedContent ? { content: cleanedContent, error: false } : { content: "有点问题,请重置对话", error: true };
292
292
  }
293
293
  __name(filterResponse, "filterResponse");
294
294
  function addOutputCensor(session, word, baseURL) {
@@ -311,6 +311,45 @@ async function updateUserPWithTicket(ctx, user, adjustment) {
311
311
  }
312
312
  }
313
313
  __name(updateUserPWithTicket, "updateUserPWithTicket");
314
+ function findLongestCommonSubstring(str1, str2) {
315
+ if (!str1 || !str2)
316
+ return 0;
317
+ const m = str1.length;
318
+ const n = str2.length;
319
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
320
+ let maxLength = 0;
321
+ for (let i = 1; i <= m; i++) {
322
+ for (let j = 1; j <= n; j++) {
323
+ if (str1[i - 1] === str2[j - 1]) {
324
+ dp[i][j] = dp[i - 1][j - 1] + 1;
325
+ maxLength = Math.max(maxLength, dp[i][j]);
326
+ }
327
+ }
328
+ }
329
+ return maxLength;
330
+ }
331
+ __name(findLongestCommonSubstring, "findLongestCommonSubstring");
332
+ function countCommonChars(str1, str2) {
333
+ const chars1 = /* @__PURE__ */ new Map();
334
+ const chars2 = /* @__PURE__ */ new Map();
335
+ for (const char of str1) {
336
+ if (/[\u4e00-\u9fff]/.test(char)) {
337
+ chars1.set(char, (chars1.get(char) || 0) + 1);
338
+ }
339
+ }
340
+ for (const char of str2) {
341
+ if (/[\u4e00-\u9fff]/.test(char)) {
342
+ chars2.set(char, (chars2.get(char) || 0) + 1);
343
+ }
344
+ }
345
+ let commonCount = 0;
346
+ for (const [char, count1] of chars1) {
347
+ const count2 = chars2.get(char) || 0;
348
+ commonCount += Math.min(count1, count2);
349
+ }
350
+ return commonCount;
351
+ }
352
+ __name(countCommonChars, "countCommonChars");
314
353
 
315
354
  // src/api.ts
316
355
  var logger2 = new import_koishi2.Logger("satori-api");
@@ -607,7 +646,7 @@ var MemoryManager = class {
607
646
  return acc;
608
647
  }, []);
609
648
  const filtered = sentences.filter((sentence) => !filterWords.some((word) => sentence.includes(word))).join("");
610
- const result = filtered.replace(/([,、])\1+/g, "$1").replace(/^[,。!?;,.!?]+/, "").trim();
649
+ const result = filtered.replace(/([,、…])\1+/g, "$1").replace(/^[,。!?;,.!?]+/, "").trim();
611
650
  return result || content;
612
651
  }
613
652
  // 更新短期记忆
@@ -702,22 +741,12 @@ var MemoryManager = class {
702
741
  calculateMatchScore(content, keywords) {
703
742
  if (keywords.length === 0)
704
743
  return { score: 0, count: 0 };
705
- const escapedKeywords = keywords.map((k) => escapeRegExp(k));
706
- const regex = new RegExp(escapedKeywords.join("|"), "gi");
707
- const chineseRegex = /[\u4e00-\u9fa5]/g;
708
- const regex2 = new RegExp(`[${this.charactersToRemove.join("")}]`, "g");
709
- content = content.replace(regex2, "");
710
- const matches = content.match(regex) || [];
711
- let count = 0;
712
- matches.forEach((match) => {
713
- const chineseCount = (match.match(chineseRegex) || []).length;
714
- const englishCount = match.length - chineseCount;
715
- count += chineseCount * 2 + englishCount;
716
- });
717
- const totalChinese = (content.match(chineseRegex) || []).length;
718
- const totalEnglish = content.length - totalChinese;
719
- const totalWeight = totalChinese * 2 + totalEnglish;
720
- const ratio = totalWeight > 0 ? count / totalWeight : 0;
744
+ const regex = new RegExp(`[${this.charactersToRemove.join("")}]`, "g");
745
+ content = content.replace(regex, "");
746
+ const Keyword = keywords.map((k) => escapeRegExp(k)).join("");
747
+ const length = findLongestCommonSubstring(content, Keyword);
748
+ const count = countCommonChars(content, Keyword);
749
+ const ratio = (length * length + count) / content.length;
721
750
  return { score: ratio, count };
722
751
  }
723
752
  // 格式化匹配结果
@@ -732,7 +761,8 @@ var MemoryManager = class {
732
761
  matched = matched.slice(0, topN < matched.length ? topN : matched.length);
733
762
  if (type === "common") {
734
763
  const result = `${prefixMap[type]}{
735
- ${matched.map((entry) => entry.content).join("\n")} ${date}
764
+ ${matched.map((entry) => entry.content).join("\n")}
765
+ ${date}
736
766
  }
737
767
  `;
738
768
  return result;
@@ -1028,7 +1058,7 @@ function createMiddleware(ctx, sat, config) {
1028
1058
  return async (session, next) => {
1029
1059
  if (config.enable_favorability && config.enable_warning && session.channelId === config.warning_group)
1030
1060
  sat.getWarningList(session);
1031
- sat.broadcastManager.seedBroadcast(session);
1061
+ await sat.broadcastManager.seedBroadcast(session);
1032
1062
  if (!isSpecialMessage(session))
1033
1063
  await sat.handleChannelMemoryManager(session);
1034
1064
  if (config.private && isPrivateSession(session)) {
@@ -1297,12 +1327,11 @@ var UserPortraitManager = class {
1297
1327
  return [{
1298
1328
  role: "user",
1299
1329
  content: `你是一个角色扮演智能体工作流中的一环,请根据以下信息更新用户画像。需包括:
1300
- 1. 基本信息(性别、年龄、生日等)
1330
+ 1. 基本信息(性别、年龄、生日等,不要遗漏或改动已有的信息)
1301
1331
  2. 生活习惯(作息等)
1302
1332
  3. 反感点(如有)
1303
1333
  4. 对AI的期望(从对话历史中推断)
1304
1334
  5. 用户希望记住的信息(如有)
1305
- 6. 需要短期记忆的信息(如有)
1306
1335
 
1307
1336
  用户名:${user.usersname}
1308
1337
  你与用户的关系:${level}
@@ -1315,9 +1344,7 @@ ${history}
1315
1344
  注意事项:
1316
1345
  ·用户一定是人类,其他情况是角色扮演
1317
1346
  ·根据发言修正历史画像内容,但是历史画像中与发言无关的部分必须保留
1318
- ·除非用户要求,否则不要删除基本信息中的内容
1319
1347
  ·尤其注意用户说“记住”的部分,可能是用户希望记录的信息
1320
- ·历史画像中的需要短期记忆的信息不需要保留,只需要删除旧的添加新的
1321
1348
  ·因为在角色扮演中,用户可能会说出不符合事实的信息,需要根据事实推断,避免盲目相信或主观臆测
1322
1349
  ·使用尽可能简洁的表达
1323
1350
  ·保留不确定性的表述(如"可能"、"似乎"、"用户自称")
@@ -1453,7 +1480,6 @@ var MoodManager = class {
1453
1480
  const userId = user.userid;
1454
1481
  if (!this.moodMap.has(userId))
1455
1482
  this.initUser(userId);
1456
- await this.checkDailyReset(userId);
1457
1483
  let effect = this.config.value_of_input_mood;
1458
1484
  switch (favorabilityLevel) {
1459
1485
  case "厌恶":
@@ -1489,7 +1515,6 @@ var MoodManager = class {
1489
1515
  const userId = user.userid;
1490
1516
  if (!this.moodMap.has(userId))
1491
1517
  this.initUser(userId);
1492
- await this.checkDailyReset(userId);
1493
1518
  let effect = this.config.value_of_output_mood;
1494
1519
  switch (favorabilityLevel) {
1495
1520
  case "厌恶":
@@ -1519,7 +1544,7 @@ var MoodManager = class {
1519
1544
  return;
1520
1545
  }
1521
1546
  // 应用心情变化
1522
- applyMoodChange(user, delta) {
1547
+ async applyMoodChange(user, delta) {
1523
1548
  if (!this.config.enable_mood)
1524
1549
  return;
1525
1550
  const userId = user.userid;
@@ -1533,6 +1558,7 @@ var MoodManager = class {
1533
1558
  mood: newMood,
1534
1559
  lastUpdate: Date.now()
1535
1560
  });
1561
+ await this.checkDailyReset(userId);
1536
1562
  return;
1537
1563
  }
1538
1564
  // 获取心情等级
@@ -2176,7 +2202,7 @@ var import_koishi11 = require("koishi");
2176
2202
  var logger10 = new import_koishi11.Logger("satori-game-onetouch");
2177
2203
  var SKILL_MAP = {
2178
2204
  // 基础技能
2179
- "-1": { name: "无效" },
2205
+ "-1": {},
2180
2206
  "1": { pierceDamage: 1, bleed: 3, name: "锥刺" },
2181
2207
  //特性:流血
2182
2208
  "2": { pierceDamage: 1, stun: true, name: "点穴" },
@@ -2230,7 +2256,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2230
2256
  // 当前回合数
2231
2257
  baseHP = 40;
2232
2258
  // 初始血量
2233
- playerLevelHP = 8;
2259
+ playerLevelHP = 10;
2234
2260
  // 每级增加的血量
2235
2261
  aiLevelHp = 10;
2236
2262
  // AI每级增加的血量
@@ -2271,6 +2297,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2271
2297
  例如"左 右"表示用你的左手触碰对方的右手
2272
2298
  注:
2273
2299
  发送"游戏规则"查看游戏规则
2300
+ 发送"技能列表"查看技能列表
2274
2301
  发送"结束游戏"退出游戏`);
2275
2302
  }
2276
2303
  // 结束游戏,返回结果
@@ -2292,7 +2319,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2292
2319
  left: this.player.left,
2293
2320
  right: this.player.right,
2294
2321
  hp: this.baseHP + level * this.playerLevelHP,
2295
- shield: Math.round(5 - level / 2),
2322
+ shield: Math.round(6 - level / 2),
2296
2323
  strength: 0,
2297
2324
  bleed: 0,
2298
2325
  counterAttack: 0,
@@ -2314,6 +2341,8 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2314
2341
  async processInput(input) {
2315
2342
  if (input === "游戏规则")
2316
2343
  return await wrapInHTML2(this.instuction);
2344
+ if (input === "技能列表")
2345
+ return await wrapInHTML2(this.skillList);
2317
2346
  if (this.turnCount === 0)
2318
2347
  this.initState(this.level);
2319
2348
  this.turnCount++;
@@ -2374,11 +2403,11 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2374
2403
  judgeEnd() {
2375
2404
  if (this.ai.hp <= 0) {
2376
2405
  this.winningFlag = 1 /* win */;
2377
- return `你赢了!发送结束游戏退出`;
2406
+ return `你赢了!发送"结束游戏"退出`;
2378
2407
  }
2379
2408
  if (this.player.hp <= 0) {
2380
2409
  this.winningFlag = 2 /* lose */;
2381
- return `你输了!发送结束游戏退出`;
2410
+ return `你输了!发送"结束游戏"退出`;
2382
2411
  }
2383
2412
  }
2384
2413
  processPlayerTurn(handA, handB) {
@@ -2388,7 +2417,7 @@ var OneTouchSingleGame = class extends abstractGameSingleGame {
2388
2417
  if (this.player.status === 1 /* Stunned */) {
2389
2418
  this.player.status = 2 /* lastStunned */;
2390
2419
  this.applyEffect(this.player, this.ai, SKILL_MAP["-1"], false);
2391
- return "你被眩晕,跳过回合";
2420
+ return "你被眩晕,跳过回合" + this.buildResultMessage(SKILL_MAP["-1"], false, this.player, this.ai);
2392
2421
  }
2393
2422
  const sum = (handA + handB) % 10;
2394
2423
  this.player[handA === this.player.left ? "left" : "right"] = sum;
@@ -2521,7 +2550,7 @@ ${bonusMessage}` : "");
2521
2550
  if (this.ai.status === 1 /* Stunned */) {
2522
2551
  this.ai.status = 2 /* lastStunned */;
2523
2552
  this.applyEffect(this.ai, this.player, SKILL_MAP["-1"], false);
2524
- return "被眩晕,跳过回合,本回合分数奖励继承到下一回合";
2553
+ return "被眩晕,跳过回合,本回合分数奖励继承到下一回合" + this.buildResultMessage(SKILL_MAP["-1"], false, this.ai, this.player);
2525
2554
  }
2526
2555
  const sum = (handA + handB) % 10;
2527
2556
  this.ai[handA === this.ai.left ? "left" : "right"] = sum;
@@ -2873,10 +2902,10 @@ ${bonusMessage}` : "");
2873
2902
  if (this.lastScore < 9e4 && Score > 9e4) {
2874
2903
  return "我觉得你要输了哦~";
2875
2904
  }
2876
- if (this.lastScore < 0 && Score > 0) {
2905
+ if (this.lastScore < 0 && Score > 0 && Math.random() > 0.5) {
2877
2906
  return "局势发生变化了呢~";
2878
2907
  }
2879
- if (Score - this.lastScore > 10 && Math.random() > 0.5)
2908
+ if (Score - this.lastScore > 10 && Math.random() > 0.8)
2880
2909
  return "看招!";
2881
2910
  }
2882
2911
  instuction = `游戏说明:
@@ -2889,9 +2918,23 @@ ${bonusMessage}` : "");
2889
2918
  当自身两手手势相同时,无论选择左手还是右手最终发生变化的都是左手。
2890
2919
  玩家初始有"${this.baseHP} + ${this.playerLevelHP} * 难度"血量。
2891
2920
  ai初始有"${this.baseHP} + ${this.aiLevelHp} * 难度"血量。
2892
- 玩家初始有"5 - 难度 / 2"护盾,四舍五入取整。
2921
+ 玩家初始有"6 - 难度 / 2"护盾,四舍五入取整。
2893
2922
  血量没有上限,率先将对方血量减到零的人获胜:
2894
- 具体的技能设计如下:{
2923
+
2924
+ 游戏名称说明:
2925
+ 流血效果:每次到自己回合结束时,收到流血层数点伤害,不可被护盾阻挡,然后流血层数减1;
2926
+ 护盾效果:每一层护盾可阻挡下一次受到的普通伤害,如果伤害来源是组合技则消耗两层护盾(若仅有一层则消耗一层)。护盾上限为五层。
2927
+ 眩晕效果:跳过自己的下一个回合,若上回合已经被眩晕,则本回合不受眩晕影响(即不可被连续眩晕)
2928
+ 弱眩晕效果:若对方没有护盾,则眩晕对方一回合
2929
+ 反击:下一回合若对方行动中有攻击,则对方攻击前受到反击层数点穿刺伤害,此效果受力量加成,且仅持续一回合
2930
+ 穿刺伤害:不被护盾影响的伤害(不会消耗护盾)
2931
+ 力量效果:每有一点力量,每次造成的伤害+1,若为负数则减一
2932
+ 易伤效果:受到的普通伤害+50%,自己的回合结束时减少一层
2933
+ 组合技:在触碰完成后,若你的两只手手势符合组合技条件,则触发组合技,组合技的效果会覆盖普通技能的效果,组合无序
2934
+ bonus: 当行动导致关键效果时,获得额外奖励,在ai回合结束时结算,若ai被眩晕则奖励继承到下一回合
2935
+ `;
2936
+ skillList = `
2937
+ 具体的技能设计如下:
2895
2938
  一:${SKILL_MAP["1"].name}:造成${SKILL_MAP["1"].pierceDamage}穿刺伤害,${SKILL_MAP["1"].bleed}流血;
2896
2939
  二:${SKILL_MAP["2"].name}:造成${SKILL_MAP["2"].pierceDamage}穿刺伤害,眩晕对方;
2897
2940
  三:${SKILL_MAP["3"].name}:造成${SKILL_MAP["3"].damage}伤害,获得${SKILL_MAP["3"].counterAttack}层反击;
@@ -2921,18 +2964,6 @@ ${bonusMessage}` : "");
2921
2964
  三+七:${SKILL_MAP["3+7"].name}:获得${SKILL_MAP["3+7"].counterAttack}层反击,破坏对方${SKILL_MAP["3+7"].destroyShield}层护盾,对方左手数值增加${SKILL_MAP["3+7"].addLeft},右手数值增加${SKILL_MAP["3+7"].addRight}
2922
2965
  九+十:${SKILL_MAP["0+9"].name}:逆转双方力量,然后增加自身${SKILL_MAP["0+9"].selfstrengthChange}力量,对方${SKILL_MAP["0+9"].strengthChange}力量
2923
2966
  十+十:${SKILL_MAP["0+0"].name}:对对方造成${SKILL_MAP["0+0"].bleed}流血,自身${SKILL_MAP["0+0"].selfstrengthChange}力量,然后立即结算对方全部流血
2924
- }
2925
- 注:
2926
- 流血效果:每次到自己回合结束时,收到流血层数点伤害,不可被护盾阻挡,然后流血层数减1;
2927
- 护盾效果:每一层护盾可阻挡下一次受到的普通伤害,如果伤害来源是组合技则消耗两层护盾(若仅有一层则消耗一层)。护盾上限为五层。
2928
- 眩晕效果:跳过自己的下一个回合,若上回合已经被眩晕,则本回合不受眩晕影响(即不可被连续眩晕)
2929
- 弱眩晕效果:若对方没有护盾,则眩晕对方一回合
2930
- 反击:下一回合若对方行动中有攻击,则对方攻击前受到反击层数点穿刺伤害,此效果受力量加成,且仅持续一回合
2931
- 穿刺伤害:不被护盾影响的伤害(不会消耗护盾)
2932
- 力量效果:每有一点力量,每次造成的伤害+1,若为负数则减一
2933
- 易伤效果:受到的普通伤害+50%,自己的回合结束时减少一层
2934
- 组合技:两只手势符合组合技条件时触发组合技,组合技的效果会覆盖普通技能的效果,组合无序
2935
- bonus: 当行动导致关键效果时,获得额外奖励,在ai回合结束时结算,若ai被眩晕则奖励继承到下一回合
2936
2967
  `;
2937
2968
  };
2938
2969
  var OneTouchGame = class extends abstractGame {
@@ -3234,13 +3265,13 @@ var broadcastMessage = class {
3234
3265
  if (this.alreadySentChannels.has(channelId))
3235
3266
  return;
3236
3267
  try {
3268
+ this.alreadySentChannels.add(channelId);
3237
3269
  if (this.img) {
3238
3270
  session.send(await wrapInHTML3(this.message));
3239
3271
  } else {
3240
3272
  session.send(this.message);
3241
3273
  }
3242
3274
  logger12.info(`向群 ${channelId} 广播: ${this.message}`);
3243
- this.alreadySentChannels.add(channelId);
3244
3275
  } catch (error) {
3245
3276
  logger12.error(`广播失败 ${channelId}: ${error}`);
3246
3277
  }
@@ -3663,7 +3694,7 @@ ${payload}`);
3663
3694
  getThinkingPrompt(user, prompt) {
3664
3695
  const reasonerPrompt = this.config.reasoner_prompt;
3665
3696
  const promptForNoReasoner = `#请你在回复时先进行分析思考,并且模仿思维链的模式输出思考内容,${reasonerPrompt};
3666
- #你在思考时必须以 "<think>" 开头, "</think>" 结尾。仔细揣摩用户意图,完整输出思考内容后在输出正式的回复内容;
3697
+ #你在思考时必须以 "<think>" 开头, "</think>" 结尾。仔细揣摩用户意图,**完整输出思考内容后**再输出正式的回复内容;
3667
3698
  #注意:你的正式回复内容必须使用“<p>”开头,使用“</p>”结尾,并且无论如何都要把标签输出完整
3668
3699
  `;
3669
3700
  const promptForReasoner = `#你在思考时必须以 "嗯" 开头。仔细揣摩用户意图,思考结束后返回符合要求的回复。
@@ -3765,7 +3796,7 @@ ${useNoReasoner ? promptForNoReasoner : promptForReasoner}
3765
3796
  const filePath = path7.join(this.config.dataDir, "common_sense.txt");
3766
3797
  await this.memoryManager.saveLongTermMemory(session, [{
3767
3798
  role: "user",
3768
- content
3799
+ content: content.replace(/\"/g, "'").trim()
3769
3800
  }], filePath);
3770
3801
  return session.text("commands.sat.common_sense.messages.succeed", [content]);
3771
3802
  }
package/lib/mood.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare class MoodManager {
9
9
  private checkDailyReset;
10
10
  handleInputMoodChange(user: User, favorabilityLevel: string): Promise<void>;
11
11
  handleOutputMoodChange(user: User, favorabilityLevel: string): Promise<void>;
12
- applyMoodChange(user: User, delta: number): void;
12
+ applyMoodChange(user: User, delta: number): Promise<void>;
13
13
  getMoodLevel(userId: string): string;
14
14
  getMoodValue(userId: string): number;
15
15
  generateMoodPrompt(userId: string): string;
package/lib/utils.d.ts CHANGED
@@ -29,3 +29,5 @@ export declare function filterResponse(prompt: string, words: string[]): {
29
29
  };
30
30
  export declare function addOutputCensor(session: Session, word: string, baseURL: string): void;
31
31
  export declare function updateUserPWithTicket(ctx: Context, user: User, adjustment: number): Promise<void>;
32
+ export declare function findLongestCommonSubstring(str1: string, str2: string): number;
33
+ export declare function countCommonChars(str1: string, str2: string): number;
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.5",
4
+ "version": "1.3.7",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "types": "lib/index.d.ts",