koishi-plugin-starfx-bot 0.5.1 → 0.7.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/lib/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { Context, h, Schema, Session } from 'koishi';
1
+ import { Context, h, Logger, Schema, Session } from 'koishi';
2
2
  export declare const name = "starfx-bot";
3
3
  export declare let baseDir: string;
4
4
  export declare let assetsDir: string;
5
+ export declare const starfxLogger: Logger;
6
+ export declare const repeatContextMap: Map<string, [string, number]>;
5
7
  export interface Config {
6
8
  openLock: boolean;
7
9
  openSold: boolean;
@@ -10,6 +12,10 @@ export interface Config {
10
12
  atNotSayOther: boolean;
11
13
  atNotSayProperty: number;
12
14
  atNotSayOtherProperty: number;
15
+ record: boolean;
16
+ openRepeat: boolean;
17
+ minRepeatTimes: number;
18
+ repeatPossibility: number;
13
19
  replyBot: string;
14
20
  iLoveYou: boolean;
15
21
  }
@@ -17,26 +23,42 @@ export declare const Config: Schema<Schemastery.ObjectS<{
17
23
  openLock: Schema<boolean, boolean>;
18
24
  openSold: Schema<boolean, boolean>;
19
25
  bangdreamBorder: Schema<boolean, boolean>;
26
+ record: Schema<boolean, boolean>;
20
27
  atNotSay: Schema<boolean, boolean>;
21
28
  atNotSayProperty: Schema<number, number>;
22
29
  atNotSayOther: Schema<boolean, boolean>;
23
30
  atNotSayOtherProperty: Schema<number, number>;
31
+ openRepeat: Schema<boolean, boolean>;
32
+ minRepeatTimes: Schema<number, number>;
33
+ repeatPossibility: Schema<number, number>;
24
34
  iLoveYou: Schema<boolean, boolean>;
25
35
  replyBot: Schema<"关闭" | "无需at" | "必须at", "关闭" | "无需at" | "必须at">;
26
36
  }>, {
27
37
  openLock: boolean;
28
38
  openSold: boolean;
29
39
  bangdreamBorder: boolean;
40
+ record: boolean;
30
41
  atNotSay: boolean;
31
42
  atNotSayProperty: number;
32
43
  atNotSayOther: boolean;
33
44
  atNotSayOtherProperty: number;
45
+ openRepeat: boolean;
46
+ minRepeatTimes: number;
47
+ repeatPossibility: number;
34
48
  iLoveYou: boolean;
35
49
  replyBot: "关闭" | "无需at" | "必须at";
36
50
  } & import("cosmokit").Dict>;
37
51
  export declare const usage = "<h5>StarFreedomX\u7684\u81EA\u7528\u63D2\u4EF6 \u653E\u4E86\u4E00\u4E9B\u5C0F\u529F\u80FD</h5>\n ";
38
52
  export declare function apply(ctx: Context, cfg: Config): void;
39
- export declare function getImageSrc(session: Session, param: string): Promise<string>;
53
+ export declare function addRecord(ctx: Context, gid: string, avatarUrl: string): Promise<string>;
54
+ export declare function getRecord(gid: string): Promise<string>;
55
+ export declare function getImageSrc(session: Session, param: string, option?: {
56
+ number?: boolean;
57
+ img?: boolean;
58
+ at?: boolean;
59
+ noParam?: boolean;
60
+ quote?: boolean;
61
+ }): Promise<string>;
40
62
  export declare function handleBanGDreamConfig(ctx: Context, options: any): Promise<{
41
63
  color: string;
42
64
  band: string;
package/lib/index.js CHANGED
@@ -41,14 +41,18 @@ var require_zh_CN = __commonJS({
41
41
  var src_exports = {};
42
42
  __export(src_exports, {
43
43
  Config: () => Config,
44
+ addRecord: () => addRecord,
44
45
  apply: () => apply,
45
46
  assetsDir: () => assetsDir,
46
47
  baseDir: () => baseDir,
47
48
  drawLock: () => drawLock,
48
49
  drawSold: () => drawSold,
49
50
  getImageSrc: () => getImageSrc,
51
+ getRecord: () => getRecord,
50
52
  handleBanGDreamConfig: () => handleBanGDreamConfig,
51
53
  name: () => name,
54
+ repeatContextMap: () => repeatContextMap,
55
+ starfxLogger: () => starfxLogger,
52
56
  usage: () => usage
53
57
  });
54
58
  module.exports = __toCommonJS(src_exports);
@@ -56,18 +60,25 @@ var import_koishi = require("koishi");
56
60
  var import_sharp = __toESM(require("sharp"));
57
61
  var fs = __toESM(require("fs"));
58
62
  var import_jimp = require("jimp");
63
+ var import_node_path = __toESM(require("node:path"));
59
64
  var name = "starfx-bot";
60
65
  var baseDir;
61
66
  var assetsDir;
67
+ var starfxLogger = new import_koishi.Logger("starfx-bot");
68
+ var repeatContextMap = /* @__PURE__ */ new Map();
62
69
  var Config = import_koishi.Schema.intersect([
63
70
  import_koishi.Schema.object({
64
71
  openLock: import_koishi.Schema.boolean().default(true).description("开启明日方舟封印功能"),
65
72
  openSold: import_koishi.Schema.boolean().default(true).description('开启闲鱼"卖掉了"功能'),
66
73
  bangdreamBorder: import_koishi.Schema.boolean().default(true).description("开启BanG Dream!边框功能"),
74
+ record: import_koishi.Schema.boolean().default(true).description("开启群语录功能"),
67
75
  atNotSay: import_koishi.Schema.boolean().default(true).description("开启‘艾特我又不说话’功能"),
68
76
  atNotSayProperty: import_koishi.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.5).description("'艾特我又不说话'回复概率"),
69
77
  atNotSayOther: import_koishi.Schema.boolean().default(true).description("开启‘艾特他又不说话’功能"),
70
78
  atNotSayOtherProperty: import_koishi.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.5).description("'艾特他又不说话'回复概率"),
79
+ openRepeat: import_koishi.Schema.boolean().default(true).description("开启复读功能"),
80
+ minRepeatTimes: import_koishi.Schema.number().default(2).description("最少重复次数"),
81
+ repeatPossibility: import_koishi.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.3).description("复读发生概率"),
71
82
  iLoveYou: import_koishi.Schema.boolean().default(true).description("开启‘我喜欢你’功能"),
72
83
  replyBot: import_koishi.Schema.union(["关闭", "无需at", "必须at"]).default("无需at").description("回复‘我才不是机器人!’功能")
73
84
  })
@@ -98,11 +109,49 @@ function apply(ctx, cfg) {
98
109
  return import_koishi.h.image(imageBase64);
99
110
  });
100
111
  }
112
+ if (cfg.record) {
113
+ ctx.command("投稿 [param]").action(async ({ session }, param) => {
114
+ const imageSrc = await getImageSrc(
115
+ session,
116
+ param,
117
+ {
118
+ img: true,
119
+ at: false,
120
+ quote: true,
121
+ noParam: false,
122
+ number: false
123
+ }
124
+ );
125
+ if (!imageSrc) {
126
+ return "请发送带图片的指令消息或引用图片消息进行投稿";
127
+ }
128
+ return await addRecord(ctx, session.gid.replace(":", "_"), imageSrc);
129
+ });
130
+ }
101
131
  ctx.middleware(async (session, next) => {
102
132
  const elements = session.elements;
133
+ if (cfg.openRepeat) {
134
+ const gid = session.gid;
135
+ const content = session.content;
136
+ const groupMap = repeatContextMap.get(gid);
137
+ if (!groupMap || groupMap[0] !== content) {
138
+ repeatContextMap.set(gid, [content, 1]);
139
+ } else {
140
+ if (groupMap[1] !== -1 && ++groupMap[1] >= cfg.minRepeatTimes && import_koishi.Random.bool(cfg.repeatPossibility)) {
141
+ groupMap[1] = -1;
142
+ await session.send(content);
143
+ }
144
+ }
145
+ }
103
146
  await atNotSayReply(cfg, session, elements);
104
147
  await replyBot(cfg, session, elements);
105
148
  await iLoveYou(cfg, session, elements);
149
+ if (cfg.record && session.content?.startsWith("语录")) {
150
+ const filepath = await getRecord(session.gid.replace(":", "_"));
151
+ starfxLogger.info(`send record: ${filepath}`);
152
+ if (!filepath) return "暂无语录呢";
153
+ return import_koishi.h.image(filepath);
154
+ }
106
155
  return next();
107
156
  });
108
157
  function initAssets() {
@@ -121,27 +170,80 @@ function apply(ctx, cfg) {
121
170
  __name(initAssets, "initAssets");
122
171
  }
123
172
  __name(apply, "apply");
124
- async function getImageSrc(session, param) {
125
- if (param?.length && param?.length === String(Number(param))?.length) {
173
+ async function addRecord(ctx, gid, avatarUrl) {
174
+ const recordDir = `${assetsDir}/record/${gid}`;
175
+ const avatarBuffer = await ctx.http.get(avatarUrl, { responseType: "arraybuffer" });
176
+ saveImage(avatarBuffer, recordDir);
177
+ return "添加成功!";
178
+ }
179
+ __name(addRecord, "addRecord");
180
+ async function getRecord(gid) {
181
+ const recordDir = `${assetsDir}/record/${gid}`;
182
+ const files = fs.readdirSync(recordDir);
183
+ return files?.length ? import_node_path.default.join(recordDir, import_koishi.Random.pick(files)) : null;
184
+ }
185
+ __name(getRecord, "getRecord");
186
+ function getTodayPrefix() {
187
+ const now = /* @__PURE__ */ new Date();
188
+ const yyyy = now.getFullYear();
189
+ const mm = String(now.getMonth() + 1).padStart(2, "0");
190
+ const dd = String(now.getDate()).padStart(2, "0");
191
+ return `${yyyy}${mm}${dd}`;
192
+ }
193
+ __name(getTodayPrefix, "getTodayPrefix");
194
+ function getNextSequenceNumber(directory, prefix) {
195
+ const files = fs.existsSync(directory) ? fs.readdirSync(directory) : [];
196
+ const regex = new RegExp(`^${prefix}-(\\d+)\\.jpg$`);
197
+ let maxNum = 0;
198
+ files.forEach((file) => {
199
+ const match = file.match(regex);
200
+ if (match) {
201
+ const num = parseInt(match[1], 10);
202
+ if (num > maxNum) {
203
+ maxNum = num;
204
+ }
205
+ }
206
+ });
207
+ return maxNum + 1;
208
+ }
209
+ __name(getNextSequenceNumber, "getNextSequenceNumber");
210
+ function saveImage(arrayBuffer, directory) {
211
+ const prefix = getTodayPrefix();
212
+ const seq = getNextSequenceNumber(directory, prefix);
213
+ const filename = `${prefix}-${seq}.jpg`;
214
+ const filepath = import_node_path.default.join(directory, filename);
215
+ if (!fs.existsSync(directory)) {
216
+ fs.mkdirSync(directory, { recursive: true });
217
+ }
218
+ const buffer = Buffer.from(arrayBuffer);
219
+ fs.writeFileSync(filepath, buffer);
220
+ console.log(`已保存图片:${filepath}`);
221
+ }
222
+ __name(saveImage, "saveImage");
223
+ async function getImageSrc(session, param, option) {
224
+ const number = option?.number ?? true, img = option?.img ?? true, at = option?.at ?? true, noParam = option?.noParam ?? true, quote = option?.quote ?? true;
225
+ if (number && param?.length && param?.length === String(Number(param))?.length) {
126
226
  return `https://q1.qlogo.cn/g?b=qq&nk=${param}&s=640`;
127
- } else if (!param?.length) {
227
+ } else if (noParam && !param?.length) {
128
228
  return `https://q1.qlogo.cn/g?b=qq&nk=${session.userId}&s=640`;
129
229
  }
130
230
  const elementArray = session.elements;
131
231
  for (const element of elementArray) {
132
- if (element?.type === "img") {
232
+ if (img && element?.type === "img") {
133
233
  return element?.attrs?.src;
134
- } else if (element?.type === "at" && element?.attrs?.id) {
234
+ } else if (at && element?.type === "at" && element?.attrs?.id) {
135
235
  return `https://q1.qlogo.cn/g?b=qq&nk=${element.attrs.id}&s=640`;
136
236
  }
137
237
  }
138
- const quoteElementArray = session?.quote?.elements;
139
- if (quoteElementArray?.length) {
140
- for (const element of quoteElementArray) {
141
- if (element?.type === "img") {
142
- return element?.attrs?.src;
143
- } else if (element?.type === "at" && element?.attrs?.id && element.attrs.id !== session.selfId) {
144
- return `https://q1.qlogo.cn/g?b=qq&nk=${element?.attrs?.id}&s=640`;
238
+ if (quote) {
239
+ const quoteElementArray = session?.quote?.elements;
240
+ if (quoteElementArray?.length) {
241
+ for (const element of quoteElementArray) {
242
+ if (img && element?.type === "img") {
243
+ return element?.attrs?.src;
244
+ } else if (at && element?.type === "at" && element?.attrs?.id && element.attrs.id !== session.selfId) {
245
+ return `https://q1.qlogo.cn/g?b=qq&nk=${element?.attrs?.id}&s=640`;
246
+ }
145
247
  }
146
248
  }
147
249
  }
@@ -156,14 +258,14 @@ async function handleBanGDreamConfig(ctx, options) {
156
258
  happy: ["happy", "orange", "橙", "橙色"]
157
259
  };
158
260
  const bands = {
159
- ppp: ["ppp", "Poppin'Party", "破琵琶", "步品破茶", "poppin", "popipa", "poppinparty", "ポピパ"],
160
- ag: ["ag", "Afterglow", "夕阳红", "悪蓋愚狼"],
161
- pp: ["pp", "PastelPalettes", "Pastel*Palettes", "PastelPalettes", "怕死怕累", "pastel", "palettes", "pasupare", "パスパレ", "破巣照破烈斗"],
162
- hhw: ["hhw", "ハロー、ハッピーワールド!", "Hello,HappyWorld!", "helloHappyWorld", "ハロハピ", "HelloHappyWorld!", "harohapi", "破狼法被威悪怒", "儿歌团", "好好玩"],
163
- r: ["r", "Roselia", "露世里恶", "萝", "露世裏悪", "ロゼリア", "r组", "相声团", "相声组"],
164
- ras: ["ras", "RaiseASuilen", "raise", "suilen", "ラス", "零図悪酔恋", "睡莲", "麗厨唖睡蓮", "睡蓮"],
165
- mnk: ["mnk", "モニカ", "蝶团", "蝶", "Morfonica", "毛二力", "monika", "monica"],
166
- go: ["go", "MyGO!!!!!", "MyGO!!!!!", "mygo", "我去!!!!!", "我去!!!!!", "我去", "卖狗"]
261
+ ppp: ["ppp", "poppin'Party", "破琵琶", "步品破茶", "poppin", "popipa", "poppinparty", "ポピパ"],
262
+ ag: ["ag", "afterglow", "夕阳红", "悪蓋愚狼"],
263
+ pp: ["pp", "pastelpalettes", "pastel*palettes", "pastelPalettes", "怕死怕累", "pastel", "palettes", "pasupare", "パスパレ", "破巣照破烈斗"],
264
+ hhw: ["hhw", "ハロー、ハッピーワールド!", "hello,happyworld!", "hellohappyworld", "ハロハピ", "hellohappyworld!", "harohapi", "破狼法被威悪怒", "儿歌团", "好好玩"],
265
+ r: ["r", "roselia", "露世里恶", "萝", "露世裏悪", "ロゼリア", "r组", "相声团", "相声组"],
266
+ ras: ["ras", "raiseasuilen", "raise", "suilen", "ラス", "零図悪酔恋", "睡莲", "麗厨唖睡蓮", "睡蓮"],
267
+ mnk: ["mnk", "モニカ", "蝶团", "蝶", "morfonica", "毛二力", "monika", "monica"],
268
+ go: ["go", "mygo!!!!!", "mygo!!!!!", "mygo", "我去!!!!!", "我去!!!!!", "我去", "卖狗"]
167
269
  };
168
270
  const trains = {
169
271
  "color_star": ["花后", "1", "彩", "true"],
@@ -194,9 +296,9 @@ async function handleBanGDreamConfig(ctx, options) {
194
296
  }
195
297
  const starNum = options.starNum ? parseInt(options.starNum) : 0;
196
298
  drawConfig.starNum = starNum > 0 && starNum < 10 ? starNum : 0;
197
- if (drawConfig.starType) {
299
+ if (options.train) {
198
300
  for (const [train, aliases] of Object.entries(trains)) {
199
- if (aliases.includes(options.color.toLowerCase())) {
301
+ if (aliases.includes(options.train.toLowerCase())) {
200
302
  drawConfig.starType = train;
201
303
  break;
202
304
  }
@@ -360,13 +462,17 @@ __name(drawBanGDream, "drawBanGDream");
360
462
  // Annotate the CommonJS export names for ESM import in node:
361
463
  0 && (module.exports = {
362
464
  Config,
465
+ addRecord,
363
466
  apply,
364
467
  assetsDir,
365
468
  baseDir,
366
469
  drawLock,
367
470
  drawSold,
368
471
  getImageSrc,
472
+ getRecord,
369
473
  handleBanGDreamConfig,
370
474
  name,
475
+ repeatContextMap,
476
+ starfxLogger,
371
477
  usage
372
478
  });
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "contributors": [
5
5
  "StarFreedomX <starfreedomx@outlook.com>"
6
6
  ],
7
- "version": "0.5.1",
7
+ "version": "0.7.0",
8
8
  "main": "lib/index.js",
9
9
  "typings": "lib/index.d.ts",
10
10
  "files": [
package/readme.md CHANGED
@@ -13,17 +13,19 @@ StarFreedomX机器人的小功能,自用
13
13
  * 我也喜欢你!
14
14
 
15
15
  # 更新日志
16
- | 版本 | 更新日志 |
17
- |---------|--------------------------|
18
- | `0.0.1` | 加入封印功能 |
19
- | `0.1.0` | 加入"卖掉了"功能 |
20
- | `0.2.0` | 加入"艾特我又不说话"回复功能 |
21
- | `0.3.0` | 加入"我喜欢你"和"我才不是机器人!"回复功能 |
22
- | `0.3.3` | 修订添加"我才不是机器人"是否需要at |
23
- | `0.3.4` | 分离"艾特我/他又不说话"的两个选项为单独的开关 |
24
- | `0.3.5` | 修复丢失的assets |
25
- | `0.3.6` | 添加"艾特我/他又不说话"的概率配置 |
26
- | `0.4.0` | 更改处理库为sharp |
27
- | `0.4.1` | 修复sharp处理w>h的图报错的bug |
28
- | `0.5.0` | 加入BanG Dream!边框功能 |
29
- | `0.5.1` | 对边框的参数做了限定,修复了异常输入导致的报错 |
16
+ | 版本 | 更新日志 |
17
+ |---------|-----------------------------|
18
+ | `0.0.1` | 加入封印功能 |
19
+ | `0.1.0` | 加入"卖掉了"功能 |
20
+ | `0.2.0` | 加入"艾特我又不说话"回复功能 |
21
+ | `0.3.0` | 加入"我喜欢你"和"我才不是机器人!"回复功能 |
22
+ | `0.3.3` | 修订添加"我才不是机器人"是否需要at |
23
+ | `0.3.4` | 分离"艾特我/他又不说话"的两个选项为单独的开关 |
24
+ | `0.3.5` | 修复丢失的assets |
25
+ | `0.3.6` | 添加"艾特我/他又不说话"的概率配置 |
26
+ | `0.4.0` | 更改处理库为sharp |
27
+ | `0.4.1` | 修复sharp处理w>h的图报错的bug |
28
+ | `0.5.0` | 加入BanG Dream!边框功能 |
29
+ | `0.5.1` | 对边框的参数做了限定,修复了异常输入导致的报错 |
30
+ | `0.6.0` | 加入群语录功能,修复一处bangdream绘制的bug |
31
+ | `0.6.0` | 加入复读功能 |