koishi-plugin-echo-cave 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -55,25 +55,9 @@ npm install koishi-plugin-echo-cave
55
55
  5. **查看自己的投稿**:发送命令 `cave.listen` 查看自己曾经存入的所有回声洞消息
56
56
  6. **查看自己的发言**:发送命令 `cave.trace` 查看自己曾经被他人存入的回声洞消息
57
57
 
58
- ## 📁 文件结构
59
-
60
- ```
61
- src/
62
- ├── index.ts # 插件主入口,命令注册和核心功能
63
- ├── cave-helper.ts # 回声洞消息发送辅助函数
64
- ├── forward-helper.ts # 转发消息处理辅助函数
65
- ├── media-helper.ts # 媒体文件保存辅助函数
66
- ├── msg-helper.ts # 消息发送辅助函数
67
- ├── cqcode-helper.ts # CQ 码处理辅助函数
68
- ├── onebot-helper.ts # OneBot 适配器辅助函数
69
- └── locales/
70
- └── zh-CN.json # 中文语言包
71
- ```
72
-
73
58
  ## 🔧 技术说明
74
59
 
75
60
  - 插件使用 Koishi 数据库系统存储消息记录
76
- - 图片会保存在 `data/cave/images` 目录下
77
61
  - 支持嵌套转发消息的处理
78
62
  - 自动检测重复消息,避免存储重复内容
79
63
  - 支持媒体文件大小限制
@@ -82,15 +66,15 @@ src/
82
66
 
83
67
  ## 🛠️ 配置选项
84
68
 
85
- | 配置项 | 类型 | 默认值 | 说明 |
86
- |-------|------|-------|------|
69
+ | 配置项 | 类型 | 默认值 | 说明 |
70
+ |--------------------------|---------|---------|-------------------------------|
87
71
  | `adminMessageProtection` | boolean | `false` | 开启管理员消息保护,使管理员发布的消息只能由其他管理员删除 |
88
- | `allowContributorDelete` | boolean | `true` | 允许消息投稿者删除自己投稿的消息 |
89
- | `allowSenderDelete` | boolean | `true` | 允许原始消息发送者删除自己被投稿的消息 |
90
- | `enableSizeLimit` | boolean | `false` | 启用媒体文件大小限制 |
91
- | `maxImageSize` | number | `2048` | 最大图片大小(KB) |
92
- | `maxVideoSize` | number | `512` | 最大视频大小(MB) |
93
- | `maxFileSize` | number | `512` | 最大文件大小(MB) |
72
+ | `allowContributorDelete` | boolean | `true` | 允许消息投稿者删除自己投稿的消息 |
73
+ | `allowSenderDelete` | boolean | `true` | 允许原始消息发送者删除自己被投稿的消息 |
74
+ | `enableSizeLimit` | boolean | `false` | 启用媒体文件大小限制 |
75
+ | `maxImageSize` | number | `2048` | 最大图片大小(KB) |
76
+ | `maxVideoSize` | number | `512` | 最大视频大小(MB) |
77
+ | `maxFileSize` | number | `512` | 最大文件大小(MB) |
94
78
 
95
79
  ## 📝 注意事项
96
80
 
@@ -1,4 +1,5 @@
1
1
  import { Context, Session } from 'koishi';
2
+ export declare function getUserIdFromNickname(session: Session, nickname: string, userId: number): Promise<number>;
2
3
  export declare function getUserName(ctx: Context, session: Session, userId: string): Promise<string>;
3
4
  /**
4
5
  * 检查用户是否属于指定群组
@@ -0,0 +1,14 @@
1
+ import { Schema } from 'koishi';
2
+ export interface Config {
3
+ adminMessageProtection?: boolean;
4
+ allowContributorDelete?: boolean;
5
+ allowSenderDelete?: boolean;
6
+ deleteMediaWhenDeletingMsg?: boolean;
7
+ enableSizeLimit?: boolean;
8
+ maxImageSize?: number;
9
+ maxVideoSize?: number;
10
+ maxFileSize?: number;
11
+ maxRecordSize?: number;
12
+ useBase64ForMedia?: boolean;
13
+ }
14
+ export declare const Config: Schema<Config>;
@@ -0,0 +1,3 @@
1
+ import { Config } from '../../config/config';
2
+ import { Context, Session } from 'koishi';
3
+ export declare function addCave(ctx: Context, session: Session, cfg: Config, userIds?: string[]): Promise<string>;
@@ -0,0 +1,4 @@
1
+ import { Config } from '../../config/config';
2
+ import { Context, Session } from 'koishi';
3
+ export declare function deleteCave(ctx: Context, session: Session, cfg: Config, id: number): Promise<string>;
4
+ export declare function deleteCaves(ctx: Context, session: Session, cfg: Config, ids: number[]): Promise<string>;
@@ -0,0 +1,5 @@
1
+ import { Config } from '../../config/config';
2
+ import { Context, Session } from 'koishi';
3
+ export declare function getCaveListByUser(ctx: Context, session: Session): Promise<string>;
4
+ export declare function getCaveListByOriginUser(ctx: Context, session: Session): Promise<string>;
5
+ export declare function getCave(ctx: Context, session: Session, cfg: Config, id: number): Promise<string>;
@@ -0,0 +1,2 @@
1
+ import { Context, Session } from 'koishi';
2
+ export declare function bindUsersToCave(ctx: Context, session: Session, id: number, userIds: string[]): Promise<string>;
@@ -0,0 +1,2 @@
1
+ import { Context, Session } from 'koishi';
2
+ export declare function searchCave(ctx: Context, session: Session, userIds: number): Promise<string>;
@@ -1,4 +1,5 @@
1
- import { Config, EchoCave } from './index';
1
+ import { Config } from '../../config/config';
2
+ import { EchoCave } from '../../index';
2
3
  import { Context, Session } from 'koishi';
3
4
  export declare function sendCaveMsg(ctx: Context, session: Session, caveMsg: EchoCave, cfg: Config): Promise<void>;
4
5
  export declare function formatDate(date: Date): string;
@@ -1,4 +1,4 @@
1
- import { Config } from './index';
1
+ import { Config } from '../../config/config';
2
2
  import { CQCode } from '@pynickle/koishi-plugin-adapter-onebot';
3
3
  import { Message } from '@pynickle/koishi-plugin-adapter-onebot/lib/types';
4
4
  import { Context, Session } from 'koishi';
@@ -1,4 +1,4 @@
1
- import { Config } from './index';
1
+ import { Config } from '../../config/config';
2
2
  import { CQCode } from '@pynickle/koishi-plugin-adapter-onebot';
3
3
  import { Context } from 'koishi';
4
4
  export declare function processMessageContent(ctx: Context, msg: CQCode[], cfg: Config): Promise<CQCode[]>;
package/lib/index.cjs CHANGED
@@ -51,6 +51,10 @@ var require_zh_CN = __commonJS({
51
51
  noMsgWithId: "\u{1F50D} \u672A\u627E\u5230\u8BE5 ID \u7684\u56DE\u58F0\u6D1E\u6D88\u606F",
52
52
  noTemplatesConfigured: "\u274C \u672A\u914D\u7F6E\u56DE\u58F0\u6D1E\u6A21\u677F\uFF0C\u8BF7\u5148\u914D\u7F6E\u6A21\u677F\uFF01"
53
53
  },
54
+ user: {
55
+ invalidAllMention: "\u274C \u4E0D\u652F\u6301 @\u5168\u4F53\u6210\u5458\uFF0C\u8BF7\u76F4\u63A5\u6307\u5B9A\u5177\u4F53\u7528\u6237\uFF01",
56
+ userNotInGroup: "\u274C \u63D0\u4F9B\u7684\u7528\u6237 ID \u4E0D\u5168\u5C5E\u4E8E\u8BE5\u7FA4\u7EC4\uFF01"
57
+ },
54
58
  templates: {
55
59
  forward: [
56
60
  "\u{1F300} \u56DE\u58F0\u6D1E #{id}\n\n\u4E00\u5219\u56DE\u58F0\u4ECE\u65F6\u5149\u4E2D\u98D8\u6765\u2014\u2014\n\u{1F4C5} {date} \xB7 \u6765\u81EA @{originName} \n\u{1F4EE} \u7531 @{userName} \u6295\u9012",
@@ -96,9 +100,7 @@ var require_zh_CN = __commonJS({
96
100
  noMsgQuoted: "\u{1F4A1} \u8BF7\u5F15\u7528\u4E00\u6761\u6D88\u606F\u540E\u518D\u4F7F\u7528\u6B64\u547D\u4EE4\uFF01",
97
101
  existingMsg: "\u267B\uFE0F \u8BE5\u6D88\u606F\u5DF2\u5B58\u5728\u4E8E\u56DE\u58F0\u6D1E\u7A74\u4E2D\uFF01",
98
102
  msgSaved: "\u2705 \u56DE\u58F0\u6D1E\u6D88\u606F\u5DF2\u6210\u529F\u5B58\u5165\uFF0C\u6D88\u606F ID\uFF1A{0}",
99
- msgFailedToSave: "\u274C \u56DE\u58F0\u6D1E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF01",
100
- userNotInGroup: "\u274C \u63D0\u4F9B\u7684\u7528\u6237 ID \u4E0D\u5168\u5C5E\u4E8E\u8BE5\u7FA4\u7EC4\uFF01",
101
- invalidAllMention: "\u274C \u4E0D\u652F\u6301 @\u5168\u4F53\u6210\u5458\uFF0C\u8BF7\u76F4\u63A5\u6307\u5B9A\u5177\u4F53\u7528\u6237\uFF01"
103
+ msgFailedToSave: "\u274C \u56DE\u58F0\u6D1E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF01"
102
104
  }
103
105
  },
104
106
  "cave.drop": {
@@ -125,8 +127,6 @@ var require_zh_CN = __commonJS({
125
127
  description: "\u641C\u7D22\u4E0E\u6307\u5B9A\u7528\u6237\u76F8\u5173\u7684\u56DE\u58F0\u6D1E",
126
128
  messages: {
127
129
  noUserIdProvided: "\u274C \u8BF7\u63D0\u4F9B\u8981\u641C\u7D22\u7684\u7528\u6237 ID\uFF01",
128
- invalidAllMention: "\u274C \u4E0D\u652F\u6301 @\u5168\u4F53\u6210\u5458\uFF0C\u8BF7\u76F4\u63A5\u6307\u5B9A\u5177\u4F53\u7528\u6237\uFF01",
129
- userNotInGroup: "\u274C \u63D0\u4F9B\u7684\u7528\u6237 ID \u4E0D\u5168\u5C5E\u4E8E\u8BE5\u7FA4\u7EC4\uFF01",
130
130
  noValidUserIdProvided: "\u274C \u672A\u63D0\u4F9B\u6709\u6548\u7684\u7528\u6237 ID\uFF01",
131
131
  noMatchingCaves: "\u{1F50D} \u672A\u627E\u5230\u4E0E\u7528\u6237 {0} \u76F8\u5173\u7684\u56DE\u58F0\u6D1E\u6D88\u606F",
132
132
  searchResult: "\u{1F50D} \u5171\u627E\u5230 {0} \u6761\u4E0E\u8BE5\u7528\u6237\u76F8\u5173\u7684\u56DE\u58F0\u6D1E\u6D88\u606F\uFF1A\nID \u5217\u8868\uFF1A{1}"
@@ -154,9 +154,7 @@ var require_zh_CN = __commonJS({
154
154
  noIdProvided: "\u274C \u8BF7\u63D0\u4F9B\u8981\u7ED1\u5B9A\u7528\u6237\u7684\u56DE\u58F0\u6D1E\u6D88\u606F ID\uFF01",
155
155
  noUserIdProvided: "\u274C \u8BF7\u63D0\u4F9B\u8981\u7ED1\u5B9A\u7684\u7528\u6237 ID\uFF01",
156
156
  msgNotFound: "\u{1F50D} \u672A\u627E\u5230\u8BE5 ID \u7684\u56DE\u58F0\u6D1E\u6D88\u606F",
157
- userBoundSuccess: "\u2705 \u5DF2\u6210\u529F\u5C06\u7528\u6237\u7ED1\u5B9A\u5230\u56DE\u58F0\u6D1E #{0}\uFF01",
158
- userNotInGroup: "\u274C \u63D0\u4F9B\u7684\u7528\u6237 ID \u4E0D\u5168\u5C5E\u4E8E\u8BE5\u7FA4\u7EC4\uFF01",
159
- invalidAllMention: "\u274C \u4E0D\u652F\u6301 @\u5168\u4F53\u6210\u5458\uFF0C\u8BF7\u76F4\u63A5\u6307\u5B9A\u5177\u4F53\u7528\u6237\uFF01"
157
+ userBoundSuccess: "\u2705 \u5DF2\u6210\u529F\u5C06\u7528\u6237\u7ED1\u5B9A\u5230\u56DE\u58F0\u6D1E #{0}\uFF01"
160
158
  }
161
159
  }
162
160
  }
@@ -167,24 +165,44 @@ var require_zh_CN = __commonJS({
167
165
  // src/index.ts
168
166
  var index_exports = {};
169
167
  __export(index_exports, {
170
- Config: () => Config,
171
168
  apply: () => apply,
172
169
  inject: () => inject,
173
170
  name: () => name
174
171
  });
175
172
  module.exports = __toCommonJS(index_exports);
176
- var import_koishi_plugin_adapter_onebot = require("@pynickle/koishi-plugin-adapter-onebot");
173
+ var import_koishi_plugin_adapter_onebot2 = require("@pynickle/koishi-plugin-adapter-onebot");
177
174
 
178
- // src/cqcode-helper.ts
179
- var import_koishi = require("koishi");
180
- function createTextMsg(content) {
181
- return {
182
- type: "text",
183
- data: {
184
- text: content
185
- }
186
- };
175
+ // src/adapters/onebot/user.ts
176
+ async function getUserIdFromNickname(session, nickname, userId) {
177
+ const memberInfos = await session.onebot.getGroupMemberList(session.channelId);
178
+ const matches = memberInfos.filter((m) => m.nickname === nickname);
179
+ if (matches.length === 1) {
180
+ return matches[0].user_id;
181
+ }
182
+ return userId;
183
+ }
184
+ async function getUserName(ctx, session, userId) {
185
+ try {
186
+ const memberInfo = await session.onebot.getGroupMemberInfo(session.channelId, userId);
187
+ return memberInfo.card || memberInfo.nickname || userId;
188
+ } catch (error) {
189
+ ctx.logger.warn(`Failed to get group member info (userId: ${userId}):`, error);
190
+ return userId;
191
+ }
192
+ }
193
+ async function checkUsersInGroup(ctx, session, userIds) {
194
+ try {
195
+ const groupMembers = await session.onebot.getGroupMemberList(session.channelId);
196
+ const memberIds = groupMembers.map((member) => member.user_id.toString());
197
+ return userIds.every((userId) => memberIds.includes(userId));
198
+ } catch (error) {
199
+ ctx.logger.warn(`Failed to get group member list:`, error);
200
+ return false;
201
+ }
187
202
  }
203
+
204
+ // src/utils/msg/element-helper.ts
205
+ var import_koishi = require("koishi");
188
206
  function parseUserIds(userIds) {
189
207
  const parsedUserIds = [];
190
208
  for (const userId of userIds) {
@@ -210,7 +228,7 @@ function parseUserIds(userIds) {
210
228
  };
211
229
  }
212
230
 
213
- // src/media-helper.ts
231
+ // src/utils/media/media-helper.ts
214
232
  var import_axios = __toESM(require("axios"), 1);
215
233
  var import_node_fs = require("node:fs");
216
234
  var import_node_path = __toESM(require("node:path"), 1);
@@ -408,100 +426,7 @@ async function deleteMediaFilesFromMessage(ctx, content) {
408
426
  }
409
427
  }
410
428
 
411
- // src/onebot-helper.ts
412
- async function getUserName(ctx, session, userId) {
413
- try {
414
- const memberInfo = await session.onebot.getGroupMemberInfo(session.channelId, userId);
415
- return memberInfo.card || memberInfo.nickname || userId;
416
- } catch (error) {
417
- ctx.logger.warn(`Failed to get group member info (userId: ${userId}):`, error);
418
- return userId;
419
- }
420
- }
421
- async function checkUsersInGroup(ctx, session, userIds) {
422
- try {
423
- const groupMembers = await session.onebot.getGroupMemberList(session.channelId);
424
- const memberIds = groupMembers.map((member) => member.user_id.toString());
425
- return userIds.every((userId) => memberIds.includes(userId));
426
- } catch (error) {
427
- ctx.logger.warn(`Failed to get group member list:`, error);
428
- return false;
429
- }
430
- }
431
-
432
- // src/cave-helper.ts
433
- async function sendCaveMsg(ctx, session, caveMsg, cfg) {
434
- const { channelId } = session;
435
- let content = JSON.parse(caveMsg.content);
436
- if (cfg.useBase64ForMedia) {
437
- content = await Promise.all(
438
- content.map(async (element) => await convertFileUriToBase64(ctx, element))
439
- );
440
- }
441
- const date = formatDate(caveMsg.createTime);
442
- const originName = await getUserName(ctx, session, caveMsg.originUserId);
443
- const userName = await getUserName(ctx, session, caveMsg.userId);
444
- let relatedUsersFormatted = originName;
445
- if (caveMsg.relatedUsers && caveMsg.relatedUsers.length > 0) {
446
- const relatedUserNames = await Promise.all(
447
- caveMsg.relatedUsers.map(async (userId) => await getUserName(ctx, session, userId))
448
- );
449
- relatedUsersFormatted = relatedUserNames.join(", ");
450
- }
451
- const templateData = {
452
- id: caveMsg.id.toString(),
453
- date,
454
- originName,
455
- userName,
456
- relatedUsers: relatedUsersFormatted,
457
- nl: "\n"
458
- };
459
- const TEMPLATE_COUNT = 5;
460
- if (caveMsg.type === "forward") {
461
- const availableTemplates2 = [];
462
- for (let i = 0; i < TEMPLATE_COUNT; i++) {
463
- const template = session.text(`echo-cave.templates.forward.${i}`, templateData);
464
- if (template.trim() !== "") {
465
- availableTemplates2.push(template);
466
- }
467
- }
468
- if (availableTemplates2.length === 0) {
469
- await session.send(session.text("echo-cave.general.noTemplatesConfigured"));
470
- return;
471
- }
472
- const chosenTemplate2 = availableTemplates2[Math.floor(Math.random() * availableTemplates2.length)];
473
- await session.onebot.sendGroupMsg(channelId, [createTextMsg(chosenTemplate2)]);
474
- await session.onebot.sendGroupForwardMsg(channelId, content);
475
- return;
476
- }
477
- const availableTemplates = [];
478
- for (let i = 0; i < TEMPLATE_COUNT; i++) {
479
- const prefix = session.text(`echo-cave.templates.msg.${i}.prefix`, templateData);
480
- const suffix = session.text(`echo-cave.templates.msg.${i}.suffix`, templateData);
481
- if (prefix.trim() !== "" && suffix.trim() !== "") {
482
- availableTemplates.push({ prefix, suffix });
483
- }
484
- }
485
- if (availableTemplates.length === 0) {
486
- await session.send(session.text("echo-cave.general.noTemplatesConfigured"));
487
- return;
488
- }
489
- const chosenTemplate = availableTemplates[Math.floor(Math.random() * availableTemplates.length)];
490
- const last = content.at(-1);
491
- const needsNewline = last?.type === "text";
492
- content.unshift(createTextMsg(chosenTemplate.prefix));
493
- content.push(createTextMsg(`${needsNewline ? "\n\n" : ""}${chosenTemplate.suffix}`));
494
- await session.onebot.sendGroupMsg(channelId, content);
495
- }
496
- function formatDate(date) {
497
- return date.toLocaleDateString("zh-CN", {
498
- year: "numeric",
499
- month: "2-digit",
500
- day: "2-digit"
501
- });
502
- }
503
-
504
- // src/forward-helper.ts
429
+ // src/core/parser/forward-parser.ts
505
430
  async function reconstructForwardMsg(ctx, session, message, cfg) {
506
431
  return Promise.all(
507
432
  message.map(async (msg) => {
@@ -520,14 +445,6 @@ async function reconstructForwardMsg(ctx, session, message, cfg) {
520
445
  })
521
446
  );
522
447
  }
523
- async function getUserIdFromNickname(session, nickname, userId) {
524
- const memberInfos = await session.onebot.getGroupMemberList(session.channelId);
525
- const matches = memberInfos.filter((m) => m.nickname === nickname);
526
- if (matches.length === 1) {
527
- return matches[0].user_id;
528
- }
529
- return userId;
530
- }
531
448
  async function processForwardMessageContent(ctx, session, msg, cfg) {
532
449
  if (typeof msg.message === "string") {
533
450
  return msg.message;
@@ -543,7 +460,7 @@ async function processForwardMessageContent(ctx, session, msg, cfg) {
543
460
  );
544
461
  }
545
462
 
546
- // src/msg-helper.ts
463
+ // src/core/parser/msg-parser.ts
547
464
  async function processMessageContent(ctx, msg, cfg) {
548
465
  return Promise.all(
549
466
  msg.map(async (element) => {
@@ -555,131 +472,79 @@ async function processMessageContent(ctx, msg, cfg) {
555
472
  );
556
473
  }
557
474
 
558
- // src/index.ts
559
- var import_koishi_plugin_adapter_onebot2 = require("@pynickle/koishi-plugin-adapter-onebot");
560
- var import_koishi2 = require("koishi");
561
- var name = "echo-cave";
562
- var inject = ["database"];
563
- var Config = import_koishi2.Schema.object({
564
- adminMessageProtection: import_koishi2.Schema.boolean().default(false),
565
- allowContributorDelete: import_koishi2.Schema.boolean().default(true),
566
- allowSenderDelete: import_koishi2.Schema.boolean().default(true),
567
- deleteMediaWhenDeletingMsg: import_koishi2.Schema.boolean().default(true),
568
- enableSizeLimit: import_koishi2.Schema.boolean().default(false),
569
- maxImageSize: import_koishi2.Schema.number().default(2048),
570
- maxVideoSize: import_koishi2.Schema.number().default(512),
571
- maxFileSize: import_koishi2.Schema.number().default(512),
572
- maxRecordSize: import_koishi2.Schema.number().default(512),
573
- useBase64ForMedia: import_koishi2.Schema.boolean().default(false)
574
- }).i18n({
575
- "zh-CN": require_zh_CN()._config
576
- });
577
- function apply(ctx, cfg) {
578
- ctx.i18n.define("zh-CN", require_zh_CN());
579
- ctx.model.extend(
580
- "echo_cave",
581
- {
582
- id: "unsigned",
583
- channelId: "string",
584
- createTime: "timestamp",
585
- userId: "string",
586
- originUserId: "string",
587
- type: "string",
588
- content: "text",
589
- relatedUsers: "list"
590
- },
591
- {
592
- primary: "id",
593
- autoInc: true
594
- }
595
- );
596
- ctx.command("cave [id:number]").action(
597
- async ({ session }, id) => await getCave(ctx, session, cfg, id)
598
- );
599
- ctx.command("cave.echo [...userIds]").action(
600
- async ({ session }, ...userIds) => await addCave(ctx, session, cfg, userIds)
601
- );
602
- ctx.command("cave.drop <id:number>").action(
603
- async ({ session }, id) => await deleteCave(ctx, session, cfg, id)
604
- );
605
- ctx.command("cave.purge <...ids:number>").action(
606
- async ({ session }, ...ids) => await deleteCaves(ctx, session, cfg, ids)
607
- );
608
- ctx.command("cave.search <...userIds>").action(
609
- async ({ session }, ...userIds) => await searchCave(ctx, session, userIds)
610
- );
611
- ctx.command("cave.listen").action(async ({ session }) => await getCaveListByUser(ctx, session));
612
- ctx.command("cave.trace").action(
613
- async ({ session }) => await getCaveListByOriginUser(ctx, session)
614
- );
615
- ctx.command("cave.bind <id:number> <...userIds>", { authority: 4 }).action(
616
- async ({ session }, id, ...userIds) => await bindUsersToCave(ctx, session, id, userIds)
617
- );
618
- }
619
- async function getCaveListByUser(ctx, session) {
620
- if (!session.guildId) {
621
- return session.text("echo-cave.general.privateChatReminder");
622
- }
623
- const { userId, channelId } = session;
624
- const caves = await ctx.database.get("echo_cave", {
625
- userId,
626
- channelId
627
- });
628
- if (caves.length === 0) {
629
- return session.text(".noMsgContributed");
630
- }
631
- let response = session.text(".msgListHeader");
632
- for (const cave of caves) {
633
- response += session.text(".msgListItem", [cave.id, formatDate(cave.createTime)]);
634
- }
635
- return response;
636
- }
637
- async function getCaveListByOriginUser(ctx, session) {
475
+ // src/core/command/add-cave.ts
476
+ var import_koishi_plugin_adapter_onebot = require("@pynickle/koishi-plugin-adapter-onebot");
477
+ async function addCave(ctx, session, cfg, userIds) {
638
478
  if (!session.guildId) {
639
479
  return session.text("echo-cave.general.privateChatReminder");
640
480
  }
641
- const { userId, channelId } = session;
642
- const caves = await ctx.database.get("echo_cave", {
643
- originUserId: userId,
644
- channelId
645
- });
646
- if (caves.length === 0) {
647
- return session.text(".noMsgTraced");
648
- }
649
- let response = session.text(".msgListHeader");
650
- for (const cave of caves) {
651
- response += session.text(".msgListItem", [cave.id, formatDate(cave.createTime)]);
652
- }
653
- return response;
654
- }
655
- async function getCave(ctx, session, cfg, id) {
656
- if (!session.guildId) {
657
- return session.text("echo-cave.general.privateChatReminder");
481
+ if (!session.quote) {
482
+ return session.text(".noMsgQuoted");
658
483
  }
659
- let caveMsg;
660
- const { channelId } = session;
661
- if (!id) {
662
- const caves = await ctx.database.get("echo_cave", {
663
- channelId
664
- });
665
- if (caves.length === 0) {
666
- return session.text(".noMsgInCave");
484
+ const { userId, channelId, quote } = session;
485
+ const messageId = quote.id;
486
+ let parsedUserIds = [];
487
+ if (userIds && userIds.length > 0) {
488
+ ctx.logger.info(`Original userIds in addCave: ${JSON.stringify(userIds)}`);
489
+ const result = parseUserIds(userIds);
490
+ if (result.error === "invalid_all_mention") {
491
+ return session.text("echo-cave.user.invalidAllMention");
667
492
  }
668
- caveMsg = caves[Math.floor(Math.random() * caves.length)];
669
- } else {
670
- const caves = await ctx.database.get("echo_cave", {
671
- id,
672
- channelId
673
- });
674
- if (caves.length === 0) {
675
- return session.text("echo-cave.general.noMsgWithId");
493
+ parsedUserIds = result.parsedUserIds;
494
+ const isAllUsersInGroup = await checkUsersInGroup(ctx, session, parsedUserIds);
495
+ if (!isAllUsersInGroup) {
496
+ return session.text(".userNotInGroup");
676
497
  }
677
- caveMsg = caves[0];
678
498
  }
679
- await sendCaveMsg(ctx, session, caveMsg, cfg);
680
- }
681
- async function deleteCave(ctx, session, cfg, id) {
682
- if (!session.guildId) {
499
+ let content;
500
+ let type;
501
+ if (quote.elements[0].type === "forward") {
502
+ type = "forward";
503
+ const message = await reconstructForwardMsg(
504
+ ctx,
505
+ session,
506
+ await session.onebot.getForwardMsg(messageId),
507
+ cfg
508
+ );
509
+ content = JSON.stringify(message);
510
+ } else {
511
+ type = "msg";
512
+ const message = (await session.onebot.getMsg(messageId)).message;
513
+ let msgJson;
514
+ if (typeof message === "string") {
515
+ msgJson = import_koishi_plugin_adapter_onebot.CQCode.parse(message);
516
+ } else {
517
+ if (message[0].type === "video" || message[0].type === "file") {
518
+ type = "forward";
519
+ }
520
+ msgJson = message;
521
+ }
522
+ content = JSON.stringify(await processMessageContent(ctx, msgJson, cfg));
523
+ }
524
+ await ctx.database.get("echo_cave", { content }).then((existing) => {
525
+ if (existing) {
526
+ return session.text(".existingMsg");
527
+ }
528
+ });
529
+ try {
530
+ const result = await ctx.database.create("echo_cave", {
531
+ channelId,
532
+ createTime: /* @__PURE__ */ new Date(),
533
+ userId,
534
+ originUserId: quote.user.id,
535
+ type,
536
+ content,
537
+ relatedUsers: parsedUserIds || []
538
+ });
539
+ return session.text(".msgSaved", [result.id]);
540
+ } catch (error) {
541
+ return session.text(".msgFailedToSave");
542
+ }
543
+ }
544
+
545
+ // src/core/command/delete-cave.ts
546
+ async function deleteCave(ctx, session, cfg, id) {
547
+ if (!session.guildId) {
683
548
  return session.text("echo-cave.general.privateChatReminder");
684
549
  }
685
550
  if (!id) {
@@ -771,73 +636,154 @@ async function deleteCaves(ctx, session, cfg, ids) {
771
636
  return session.text(".msgDeletePartial", [failedIds.join(", ")]);
772
637
  }
773
638
  }
774
- async function addCave(ctx, session, cfg, userIds) {
775
- if (!session.guildId) {
776
- return session.text("echo-cave.general.privateChatReminder");
777
- }
778
- if (!session.quote) {
779
- return session.text(".noMsgQuoted");
780
- }
781
- const { userId, channelId, quote } = session;
782
- const messageId = quote.id;
783
- let parsedUserIds = [];
784
- if (userIds && userIds.length > 0) {
785
- ctx.logger.info(`Original userIds in addCave: ${JSON.stringify(userIds)}`);
786
- const result = parseUserIds(userIds);
787
- if (result.error === "invalid_all_mention") {
788
- return session.text(".invalidAllMention");
789
- }
790
- parsedUserIds = result.parsedUserIds;
791
- const isAllUsersInGroup = await checkUsersInGroup(ctx, session, parsedUserIds);
792
- if (!isAllUsersInGroup) {
793
- return session.text(".userNotInGroup");
639
+
640
+ // src/utils/msg/cqcode-helper.ts
641
+ function createTextMsg(content) {
642
+ return {
643
+ type: "text",
644
+ data: {
645
+ text: content
794
646
  }
647
+ };
648
+ }
649
+
650
+ // src/core/formatter/msg-formatter.ts
651
+ async function sendCaveMsg(ctx, session, caveMsg, cfg) {
652
+ const { channelId } = session;
653
+ let content = JSON.parse(caveMsg.content);
654
+ if (cfg.useBase64ForMedia) {
655
+ content = await Promise.all(
656
+ content.map(async (element) => await convertFileUriToBase64(ctx, element))
657
+ );
795
658
  }
796
- let content;
797
- let type;
798
- if (quote.elements[0].type === "forward") {
799
- type = "forward";
800
- const message = await reconstructForwardMsg(
801
- ctx,
802
- session,
803
- await session.onebot.getForwardMsg(messageId),
804
- cfg
659
+ const date = formatDate(caveMsg.createTime);
660
+ const originName = await getUserName(ctx, session, caveMsg.originUserId);
661
+ const userName = await getUserName(ctx, session, caveMsg.userId);
662
+ let relatedUsersFormatted = originName;
663
+ if (caveMsg.relatedUsers && caveMsg.relatedUsers.length > 0) {
664
+ const relatedUserNames = await Promise.all(
665
+ caveMsg.relatedUsers.map(async (userId) => await getUserName(ctx, session, userId))
805
666
  );
806
- content = JSON.stringify(message);
807
- } else {
808
- type = "msg";
809
- const message = (await session.onebot.getMsg(messageId)).message;
810
- let msgJson;
811
- if (typeof message === "string") {
812
- msgJson = import_koishi_plugin_adapter_onebot2.CQCode.parse(message);
813
- } else {
814
- if (message[0].type === "video" || message[0].type === "file") {
815
- type = "forward";
667
+ relatedUsersFormatted = relatedUserNames.join(", ");
668
+ }
669
+ const templateData = {
670
+ id: caveMsg.id.toString(),
671
+ date,
672
+ originName,
673
+ userName,
674
+ relatedUsers: relatedUsersFormatted,
675
+ nl: "\n"
676
+ };
677
+ const TEMPLATE_COUNT = 5;
678
+ if (caveMsg.type === "forward") {
679
+ const availableTemplates2 = [];
680
+ for (let i = 0; i < TEMPLATE_COUNT; i++) {
681
+ const template = session.text(`echo-cave.templates.forward.${i}`, templateData);
682
+ if (template.trim() !== "") {
683
+ availableTemplates2.push(template);
816
684
  }
817
- msgJson = message;
818
685
  }
819
- content = JSON.stringify(await processMessageContent(ctx, msgJson, cfg));
686
+ if (availableTemplates2.length === 0) {
687
+ await session.send(session.text("echo-cave.general.noTemplatesConfigured"));
688
+ return;
689
+ }
690
+ const chosenTemplate2 = availableTemplates2[Math.floor(Math.random() * availableTemplates2.length)];
691
+ await session.onebot.sendGroupMsg(channelId, [createTextMsg(chosenTemplate2)]);
692
+ await session.onebot.sendGroupForwardMsg(channelId, content);
693
+ return;
820
694
  }
821
- await ctx.database.get("echo_cave", { content }).then((existing) => {
822
- if (existing) {
823
- return session.text(".existingMsg");
695
+ const availableTemplates = [];
696
+ for (let i = 0; i < TEMPLATE_COUNT; i++) {
697
+ const prefix = session.text(`echo-cave.templates.msg.${i}.prefix`, templateData);
698
+ const suffix = session.text(`echo-cave.templates.msg.${i}.suffix`, templateData);
699
+ if (prefix.trim() !== "" && suffix.trim() !== "") {
700
+ availableTemplates.push({ prefix, suffix });
824
701
  }
702
+ }
703
+ if (availableTemplates.length === 0) {
704
+ await session.send(session.text("echo-cave.general.noTemplatesConfigured"));
705
+ return;
706
+ }
707
+ const chosenTemplate = availableTemplates[Math.floor(Math.random() * availableTemplates.length)];
708
+ const last = content.at(-1);
709
+ const needsNewline = last?.type === "text";
710
+ content.unshift(createTextMsg(chosenTemplate.prefix));
711
+ content.push(createTextMsg(`${needsNewline ? "\n\n" : ""}${chosenTemplate.suffix}`));
712
+ await session.onebot.sendGroupMsg(channelId, content);
713
+ }
714
+ function formatDate(date) {
715
+ return date.toLocaleDateString("zh-CN", {
716
+ year: "numeric",
717
+ month: "2-digit",
718
+ day: "2-digit"
825
719
  });
826
- try {
827
- const result = await ctx.database.create("echo_cave", {
828
- channelId,
829
- createTime: /* @__PURE__ */ new Date(),
830
- userId,
831
- originUserId: quote.user.id,
832
- type,
833
- content,
834
- relatedUsers: parsedUserIds || []
720
+ }
721
+
722
+ // src/core/command/get-cave.ts
723
+ async function getCaveListByUser(ctx, session) {
724
+ if (!session.guildId) {
725
+ return session.text("echo-cave.general.privateChatReminder");
726
+ }
727
+ const { userId, channelId } = session;
728
+ const caves = await ctx.database.get("echo_cave", {
729
+ userId,
730
+ channelId
731
+ });
732
+ if (caves.length === 0) {
733
+ return session.text(".noMsgContributed");
734
+ }
735
+ let response = session.text(".msgListHeader");
736
+ for (const cave of caves) {
737
+ response += session.text(".msgListItem", [cave.id, formatDate(cave.createTime)]);
738
+ }
739
+ return response;
740
+ }
741
+ async function getCaveListByOriginUser(ctx, session) {
742
+ if (!session.guildId) {
743
+ return session.text("echo-cave.general.privateChatReminder");
744
+ }
745
+ const { userId, channelId } = session;
746
+ const caves = await ctx.database.get("echo_cave", {
747
+ originUserId: userId,
748
+ channelId
749
+ });
750
+ if (caves.length === 0) {
751
+ return session.text(".noMsgTraced");
752
+ }
753
+ let response = session.text(".msgListHeader");
754
+ for (const cave of caves) {
755
+ response += session.text(".msgListItem", [cave.id, formatDate(cave.createTime)]);
756
+ }
757
+ return response;
758
+ }
759
+ async function getCave(ctx, session, cfg, id) {
760
+ if (!session.guildId) {
761
+ return session.text("echo-cave.general.privateChatReminder");
762
+ }
763
+ let caveMsg;
764
+ const { channelId } = session;
765
+ if (!id) {
766
+ const caves = await ctx.database.get("echo_cave", {
767
+ channelId
835
768
  });
836
- return session.text(".msgSaved", [result.id]);
837
- } catch (error) {
838
- return session.text(".msgFailedToSave");
769
+ if (caves.length === 0) {
770
+ return session.text(".noMsgInCave");
771
+ }
772
+ caveMsg = caves[Math.floor(Math.random() * caves.length)];
773
+ } else {
774
+ const caves = await ctx.database.get("echo_cave", {
775
+ id,
776
+ channelId
777
+ });
778
+ if (caves.length === 0) {
779
+ return session.text("echo-cave.general.noMsgWithId");
780
+ }
781
+ caveMsg = caves[0];
839
782
  }
783
+ await sendCaveMsg(ctx, session, caveMsg, cfg);
840
784
  }
785
+
786
+ // src/core/command/misc/bind-user.ts
841
787
  async function bindUsersToCave(ctx, session, id, userIds) {
842
788
  if (!session.guildId) {
843
789
  return session.text("echo-cave.general.privateChatReminder");
@@ -845,38 +791,43 @@ async function bindUsersToCave(ctx, session, id, userIds) {
845
791
  if (!id) {
846
792
  return session.text(".noIdProvided");
847
793
  }
848
- if (!userIds || userIds.length === 0) {
794
+ if (!userIds) {
849
795
  return session.text(".noUserIdProvided");
850
796
  }
851
- let parsedUserIds = [];
852
797
  const result = parseUserIds(userIds);
853
798
  if (result.error === "invalid_all_mention") {
854
- return session.text(".invalidAllMention");
799
+ return session.text("echo-cave.user.invalidAllMention");
800
+ }
801
+ const parsedUserIds = result.parsedUserIds;
802
+ if (parsedUserIds.length === 0) {
803
+ return session.text(".noValidUserIdProvided");
855
804
  }
856
- parsedUserIds = result.parsedUserIds;
857
805
  const caves = await ctx.database.get("echo_cave", id);
858
806
  if (caves.length === 0) {
859
807
  return session.text("echo-cave.general.noMsgWithId");
860
808
  }
861
809
  const isAllUsersInGroup = await checkUsersInGroup(ctx, session, parsedUserIds);
862
810
  if (!isAllUsersInGroup) {
863
- return session.text(".userNotInGroup");
811
+ return session.text("echo-cave.user.userNotInGroup");
864
812
  }
865
813
  await ctx.database.set("echo_cave", id, {
866
814
  relatedUsers: parsedUserIds
867
815
  });
868
816
  return session.text(".userBoundSuccess", [id]);
869
817
  }
818
+
819
+ // src/core/command/search-cave.ts
820
+ var import_koishi2 = require("koishi");
870
821
  async function searchCave(ctx, session, userIds) {
871
822
  if (!session.guildId) {
872
823
  return session.text("echo-cave.general.privateChatReminder");
873
824
  }
874
- if (!userIds || userIds.length === 0) {
825
+ if (!userIds) {
875
826
  return session.text(".noUserIdProvided");
876
827
  }
877
- const result = parseUserIds(userIds);
828
+ const result = parseUserIds(userIds.toString());
878
829
  if (result.error === "invalid_all_mention") {
879
- return session.text(".invalidAllMention");
830
+ return session.text("echo-cave.user.invalidAllMention");
880
831
  }
881
832
  const parsedUserIds = result.parsedUserIds;
882
833
  if (parsedUserIds.length === 0) {
@@ -884,15 +835,16 @@ async function searchCave(ctx, session, userIds) {
884
835
  }
885
836
  const isUserInGroup = await checkUsersInGroup(ctx, session, parsedUserIds);
886
837
  if (!isUserInGroup) {
887
- return session.text(".userNotInGroup");
838
+ return session.text("echo-cave.user.userNotInGroup");
888
839
  }
889
840
  const targetUserId = parsedUserIds[0];
890
841
  const { channelId } = session;
891
- const caves = await ctx.database.get("echo_cave", {
892
- channelId
893
- });
894
- const matchingCaves = caves.filter(
895
- (cave) => cave.originUserId === targetUserId || cave.relatedUsers.includes(targetUserId)
842
+ const matchingCaves = await ctx.database.get(
843
+ "echo_cave",
844
+ (row) => import_koishi2.$.and(
845
+ import_koishi2.$.eq(row.channelId, channelId),
846
+ import_koishi2.$.or(import_koishi2.$.eq(row.originUserId, targetUserId), import_koishi2.$.in(targetUserId, row.relatedUsers))
847
+ )
896
848
  );
897
849
  if (matchingCaves.length === 0) {
898
850
  return session.text(".noMatchingCaves", [targetUserId]);
@@ -901,9 +853,54 @@ async function searchCave(ctx, session, userIds) {
901
853
  const count = matchingCaves.length;
902
854
  return session.text(".searchResult", [count, caveIds]);
903
855
  }
856
+
857
+ // src/index.ts
858
+ var name = "echo-cave";
859
+ var inject = ["database"];
860
+ function apply(ctx, cfg) {
861
+ ctx.i18n.define("zh-CN", require_zh_CN());
862
+ ctx.model.extend(
863
+ "echo_cave",
864
+ {
865
+ id: "unsigned",
866
+ channelId: "string",
867
+ createTime: "timestamp",
868
+ userId: "string",
869
+ originUserId: "string",
870
+ type: "string",
871
+ content: "text",
872
+ relatedUsers: "list"
873
+ },
874
+ {
875
+ primary: "id",
876
+ autoInc: true
877
+ }
878
+ );
879
+ ctx.command("cave [id:number]").action(
880
+ async ({ session }, id) => await getCave(ctx, session, cfg, id)
881
+ );
882
+ ctx.command("cave.listen").action(async ({ session }) => await getCaveListByUser(ctx, session));
883
+ ctx.command("cave.trace").action(
884
+ async ({ session }) => await getCaveListByOriginUser(ctx, session)
885
+ );
886
+ ctx.command("cave.echo [...userIds]").action(
887
+ async ({ session }, ...userIds) => await addCave(ctx, session, cfg, userIds)
888
+ );
889
+ ctx.command("cave.drop <id:number>").action(
890
+ async ({ session }, id) => await deleteCave(ctx, session, cfg, id)
891
+ );
892
+ ctx.command("cave.purge <...ids:number>").action(
893
+ async ({ session }, ...ids) => await deleteCaves(ctx, session, cfg, ids)
894
+ );
895
+ ctx.command("cave.search <id:number>").action(
896
+ async ({ session }, id) => await searchCave(ctx, session, id)
897
+ );
898
+ ctx.command("cave.bind <id:number> <...userIds>", { authority: 4 }).action(
899
+ async ({ session }, id, ...userIds) => await bindUsersToCave(ctx, session, id, userIds)
900
+ );
901
+ }
904
902
  // Annotate the CommonJS export names for ESM import in node:
905
903
  0 && (module.exports = {
906
- Config,
907
904
  apply,
908
905
  inject,
909
906
  name
package/lib/index.d.ts CHANGED
@@ -1,20 +1,8 @@
1
1
  import '@pynickle/koishi-plugin-adapter-onebot';
2
- import { Context, Schema } from 'koishi';
2
+ import { Config } from './config/config';
3
+ import { Context } from 'koishi';
3
4
  export declare const name = "echo-cave";
4
5
  export declare const inject: string[];
5
- export interface Config {
6
- adminMessageProtection?: boolean;
7
- allowContributorDelete?: boolean;
8
- allowSenderDelete?: boolean;
9
- deleteMediaWhenDeletingMsg?: boolean;
10
- enableSizeLimit?: boolean;
11
- maxImageSize?: number;
12
- maxVideoSize?: number;
13
- maxFileSize?: number;
14
- maxRecordSize?: number;
15
- useBase64ForMedia?: boolean;
16
- }
17
- export declare const Config: Schema<Config>;
18
6
  export interface EchoCave {
19
7
  id: number;
20
8
  channelId: string;
@@ -1,4 +1,4 @@
1
- import { Config } from './index';
1
+ import { Config } from '../../config/config';
2
2
  import { Context } from 'koishi';
3
3
  export declare function saveMedia(ctx: Context, mediaElement: Record<string, any>, type: 'image' | 'video' | 'file' | 'record', cfg: Config): Promise<string>;
4
4
  export declare function processMediaElement(ctx: Context, element: any, cfg: Config): Promise<any>;
@@ -0,0 +1,6 @@
1
+ export declare function createTextMsg(content: string): {
2
+ type: string;
3
+ data: {
4
+ text: string;
5
+ };
6
+ };
@@ -0,0 +1,5 @@
1
+ export interface ParseResult {
2
+ parsedUserIds: string[];
3
+ error?: string;
4
+ }
5
+ export declare function parseUserIds(userIds: string[] | string): ParseResult;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-echo-cave",
3
3
  "description": "Group echo cave",
4
- "version": "1.17.0",
4
+ "version": "1.18.0",
5
5
  "main": "lib/index.cjs",
6
6
  "typings": "lib/index.d.ts",
7
7
  "type": "module",
@@ -1,11 +0,0 @@
1
- export interface ParseResult {
2
- parsedUserIds: string[];
3
- error?: string;
4
- }
5
- export declare function createTextMsg(content: string): {
6
- type: string;
7
- data: {
8
- text: string;
9
- };
10
- };
11
- export declare function parseUserIds(userIds: any[]): ParseResult;