koishi-plugin-subscription 0.0.4 → 0.0.6

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
@@ -41,7 +41,8 @@ declare class SubscriptionService extends Service {
41
41
  constructor(ctx: Context, config: Config);
42
42
  initMap(): void;
43
43
  private getApp;
44
- private getAccount;
44
+ getAccount(app: string, account: string): string;
45
+ getName(app: string, account: string): string;
45
46
  getAvailableAccounts(app: string): string[];
46
47
  getSubscribedGroups(app: string, account: string): Promise<string[]>;
47
48
  broadcast(app: string, account: string, content: Fragment): Promise<void>;
package/lib/index.js CHANGED
@@ -27,6 +27,21 @@ __export(src_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(src_exports);
29
29
  var import_koishi = require("koishi");
30
+
31
+ // src/retry.ts
32
+ async function retry(retries, fn, delay = 500) {
33
+ try {
34
+ return await fn();
35
+ } catch (err) {
36
+ console.log(`剩余${retries}次尝试`, err);
37
+ if (retries <= 1) throw err;
38
+ await new Promise((r) => setTimeout(r, delay));
39
+ return retry(retries - 1, fn, delay * 2);
40
+ }
41
+ }
42
+ __name(retry, "retry");
43
+
44
+ // src/index.tsx
30
45
  var import_jsx_runtime = require("@satorijs/element/jsx-runtime");
31
46
  var Config = import_koishi.Schema.intersect([
32
47
  import_koishi.Schema.object({
@@ -112,6 +127,10 @@ var SubscriptionService = class extends import_koishi.Service {
112
127
  getAccount(app, account) {
113
128
  return this.accountMap.get((app + account).toLowerCase());
114
129
  }
130
+ getName(app, account) {
131
+ app = this.getApp(app);
132
+ return this.subsConfig.apps[app].allowedAccounts.find((item) => item.id === this.getAccount(app, account))?.name;
133
+ }
115
134
  // 获取对应应用可以订阅的账号
116
135
  getAvailableAccounts(app) {
117
136
  app = this.getApp(app);
@@ -167,18 +186,39 @@ var SubscriptionService = class extends import_koishi.Service {
167
186
  return (await Promise.all(this.ctx.bots.map(async (bot) => {
168
187
  const targets = assignMap[bot.platform]?.[bot.selfId];
169
188
  if (!targets) return Promise.resolve([]);
170
- const messageIds = await bot.sendPrivateMessage(bot.selfId, content);
171
- const forwardContent = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { forward: true, children: messageIds.map((id) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { id: `${id}` })) });
172
- const sessions = targets.map(({ id, guildId, locales }) => {
173
- const session = bot.session({
189
+ try {
190
+ const selfChannelId = `private:${bot.selfId}`, session = bot.session({
174
191
  type: "message",
175
- channel: { id, type: import_koishi.Universal.Channel.Type.TEXT },
176
- guild: { id: guildId }
192
+ channel: {
193
+ id: selfChannelId,
194
+ type: import_koishi.Universal.Channel.Type.DIRECT
195
+ },
196
+ user: { id: bot.selfId }
177
197
  });
178
- session.locales = locales;
179
- return session;
180
- });
181
- return bot.broadcast(sessions, forwardContent);
198
+ const messageIds = Array.isArray(content) ? (await Promise.all(content.map(
199
+ (msg) => {
200
+ try {
201
+ return retry(3, () => bot.sendMessage(selfChannelId, msg, void 0, { session }));
202
+ } catch (e) {
203
+ return Promise.resolve([]);
204
+ }
205
+ }
206
+ ))).flat(1) : await retry(3, () => bot.sendMessage(selfChannelId, content, void 0, { session }));
207
+ const forwardContent = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { forward: true, children: messageIds.map((id) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { id: `${id}` })) });
208
+ const sessions = targets.map(({ id, guildId, locales }) => {
209
+ const session2 = bot.session({
210
+ type: "message",
211
+ channel: { id, type: import_koishi.Universal.Channel.Type.TEXT },
212
+ guild: { id: guildId }
213
+ });
214
+ session2.locales = locales;
215
+ return session2;
216
+ });
217
+ return bot.broadcast(sessions, forwardContent);
218
+ } catch (e) {
219
+ this.ctx.logger("broadcastForward").warn("broadcast error:", bot.selfId, Array.isArray(content) ? content.length : 1, e);
220
+ return Promise.resolve([]);
221
+ }
182
222
  }))).flat(1);
183
223
  }
184
224
  // 添加订阅
@@ -257,19 +297,21 @@ var SubscriptionService = class extends import_koishi.Service {
257
297
  return `${channel.platform}:${channel.id}`;
258
298
  }
259
299
  registerCommands(ctx, config) {
260
- const sub = ctx.command("subscription <app:string> [...accounts]", "添加订阅").alias("订阅").alias("添加订阅").userFields(["authority"]).action(async ({ session, options }, app, ...accounts) => {
300
+ const sub = ctx.command("subscription <app:string> [...accounts]", "添加订阅,账号可以使用 用户名 用户id 或是别名,格式:订阅 <应用名> <账号> [...更多账号]").alias("订阅").alias("添加订阅").userFields(["authority"]).example("推特订阅 爱美 伊藤彩沙").example("订阅 twitter aimi_sound").action(async ({ session, options }, app, ...accounts) => {
261
301
  if (!await this.authCheck(session, config)) {
262
302
  return "权限不足";
263
303
  }
264
304
  if (!app || !accounts || accounts.length == 0) {
265
305
  return "请指定应用名和账号,格式:订阅 <应用名> <账号> [...更多账号]";
266
306
  }
307
+ accounts;
267
308
  const groupId = this.getChannelId(session.channel);
268
309
  if (!groupId) {
269
310
  return "请在群聊中使用当前指令";
270
311
  }
271
312
  let result = "";
272
- for (const account of accounts) {
313
+ for (let account of accounts) {
314
+ app = this.getApp(app), account = this.getName(app, account);
273
315
  const { status, msg } = await this.addSubscription(app, account, groupId);
274
316
  if (status) {
275
317
  result += `成功为群组 ${groupId} 添加订阅:${app} - ${account}
@@ -281,13 +323,14 @@ var SubscriptionService = class extends import_koishi.Service {
281
323
  }
282
324
  return result;
283
325
  });
284
- const unsub = ctx.command("subscription.remove <app:string> <account:string>", "删除订阅").alias("删除订阅").alias("取消订阅").userFields(["authority"]).action(async ({ session, options }, app, account) => {
326
+ const unsub = ctx.command("subscription.remove <app:string> <account:string>", "删除订阅").alias("删除订阅").alias("取消订阅").userFields(["authority"]).example("取消推特订阅 爱美").example("取消订阅 twitter aimi_sound").action(async ({ session, options }, app, account) => {
285
327
  if (!await this.authCheck(session, config)) {
286
328
  return "权限不足";
287
329
  }
288
330
  if (!app || !account) {
289
331
  return "请指定应用名和账号,格式:取消订阅 <应用名> <账号>";
290
332
  }
333
+ app = this.getApp(app), account = this.getName(app, account);
291
334
  const groupId = this.getChannelId(session.channel);
292
335
  if (!groupId) {
293
336
  return "请在群聊中使用当前指令";
@@ -333,9 +376,7 @@ var SubscriptionService = class extends import_koishi.Service {
333
376
  }));
334
377
  }
335
378
  if (!ctx.puppeteer) {
336
- let result = `群组 ${groupId} 的订阅状态:
337
-
338
- `;
379
+ let result = ``;
339
380
  for (const app in availableAccounts) {
340
381
  result += `【${app}】
341
382
  `;
@@ -353,7 +394,23 @@ var SubscriptionService = class extends import_koishi.Service {
353
394
  return result;
354
395
  }
355
396
  const html = generateSubscriptionHTML(groupId, availableAccounts, subscribedByApp);
356
- return await ctx.puppeteer.render(html);
397
+ const page = await ctx.puppeteer.page();
398
+ try {
399
+ await page.setViewport({
400
+ width: 1e3,
401
+ height: 800
402
+ });
403
+ await page.setContent(html, {
404
+ waitUntil: "networkidle0"
405
+ });
406
+ const image = await page.screenshot({
407
+ type: "jpeg",
408
+ fullPage: true
409
+ });
410
+ return `<image url="data:image/png;base64,${image.toString("base64")}" />`;
411
+ } finally {
412
+ await page.close();
413
+ }
357
414
  });
358
415
  function generateSubscriptionHTML(groupId, availableAccounts, subscribedByApp) {
359
416
  return `
@@ -374,7 +431,7 @@ var SubscriptionService = class extends import_koishi.Service {
374
431
  padding: 20px;
375
432
  }
376
433
  .container {
377
- max-width: 1200px;
434
+ max-width: 1000px;
378
435
  margin: 0 auto;
379
436
  }
380
437
  .header {
@@ -383,18 +440,18 @@ var SubscriptionService = class extends import_koishi.Service {
383
440
  color: #2c3e50;
384
441
  }
385
442
  .header h1 {
386
- font-size: 24px;
387
- margin-bottom: 8px;
388
- font-weight: 600;
443
+ font-size: 36px;
444
+ margin-bottom: 12px;
445
+ font-weight: 900;
389
446
  }
390
447
  .header .subtitle {
391
- font-size: 14px;
448
+ font-size: 21px;
392
449
  color: #7f8c8d;
393
450
  }
394
451
  .apps-container {
395
452
  background: white;
396
- border-radius: 8px;
397
- box-shadow: 0 2px 12px rgba(0,0,0,0.08);
453
+ border-radius: 12px;
454
+ box-shadow: 0 3px 18px rgba(0,0,0,0.08);
398
455
  overflow: hidden;
399
456
  }
400
457
  .app-section {
@@ -404,46 +461,48 @@ var SubscriptionService = class extends import_koishi.Service {
404
461
  border-bottom: none;
405
462
  }
406
463
  .app-title {
407
- font-size: 16px;
464
+ font-size: 24px;
408
465
  font-weight: 600;
409
466
  color: #34495e;
410
- padding: 12px 16px;
467
+ padding: 18px 24px;
411
468
  background: #f8f9fa;
412
469
  border-bottom: 1px solid #eaeaea;
413
470
  display: flex;
414
471
  align-items: center;
415
- gap: 8px;
472
+ gap: 12px;
416
473
  }
417
474
  .app-title::before {
418
475
  content: "▸";
419
- font-size: 14px;
476
+ font-size: 21px;
420
477
  color: #3498db;
421
478
  }
422
479
  .grid {
423
480
  display: grid;
424
- grid-template-columns: repeat(3, 1fr);
481
+ grid-template-columns: repeat(2, 1fr);
425
482
  gap: 0;
426
483
  }
427
484
  .account-card {
428
- padding: 14px;
485
+ padding: 21px;
429
486
  border-right: 1px solid #f0f0f0;
430
487
  border-bottom: 1px solid #f0f0f0;
431
488
  transition: all 0.2s ease;
432
489
  position: relative;
433
- min-height: 80px;
490
+ min-height: 120px;
434
491
  display: flex;
435
492
  flex-direction: column;
436
493
  }
437
- .account-card:nth-child(odd) {
494
+ .account-card:nth-child(4n+1),
495
+ .account-card:nth-child(4n+4) {
438
496
  background: #f8f9fa;
439
497
  }
440
- .account-card:nth-child(even) {
498
+ .account-card:nth-child(4n+2),
499
+ .account-card:nth-child(4n+3) {
441
500
  background: #f0f2f5;
442
501
  }
443
502
  .account-card:hover {
444
503
  background: #e6f3ff !important;
445
504
  }
446
- .account-card:nth-child(3n) {
505
+ .account-card:nth-child(2n) {
447
506
  border-right: none;
448
507
  }
449
508
  .account-card:last-child {
@@ -453,12 +512,12 @@ var SubscriptionService = class extends import_koishi.Service {
453
512
  display: flex;
454
513
  justify-content: space-between;
455
514
  align-items: center;
456
- margin-bottom: 8px;
515
+ margin-bottom: 12px;
457
516
  flex-wrap: wrap;
458
517
  }
459
518
  .name {
460
- font-size: 14px;
461
- font-weight: 600;
519
+ font-size: 21px;
520
+ font-weight: 900;
462
521
  color: #2c3e50;
463
522
  line-height: 1.3;
464
523
  max-width: 60%;
@@ -467,12 +526,12 @@ var SubscriptionService = class extends import_koishi.Service {
467
526
  white-space: nowrap;
468
527
  }
469
528
  .id {
470
- font-size: 12px;
529
+ font-size: 21px;
471
530
  color: #5a6c7d;
472
531
  font-family: 'Courier New', monospace;
473
532
  background: #f8f9fa;
474
- padding: 3px 6px;
475
- border-radius: 3px;
533
+ padding: 4.5px 9px;
534
+ border-radius: 4.5px;
476
535
  line-height: 1.3;
477
536
  max-width: 35%;
478
537
  overflow: hidden;
@@ -485,7 +544,7 @@ var SubscriptionService = class extends import_koishi.Service {
485
544
  }
486
545
  .name-id-container.vertical .name {
487
546
  max-width: 100%;
488
- margin-bottom: 4px;
547
+ margin-bottom: 6px;
489
548
  }
490
549
  .name-id-container.vertical .id {
491
550
  max-width: 100%;
@@ -494,49 +553,49 @@ var SubscriptionService = class extends import_koishi.Service {
494
553
  .alias-status-container {
495
554
  display: flex;
496
555
  justify-content: space-between;
497
- margin-top: 8px;
556
+ margin-top: 12px;
498
557
  flex-grow: 1;
499
558
  }
500
559
  .alias-container {
501
560
  flex: 1;
502
- max-width: calc(100% - 70px);
561
+ max-width: calc(100% - 105px);
503
562
  align-self: flex-start;
504
563
  }
505
564
  .alias {
506
- font-size: 11px;
565
+ font-size: 20px;
507
566
  color: #6c757d;
508
567
  display: flex;
509
568
  flex-wrap: wrap;
510
- gap: 4px;
569
+ gap: 6px;
511
570
  align-items: flex-start;
512
571
  }
513
572
  .alias-tag {
514
573
  background: #e8f4fd;
515
574
  color: #1971c2;
516
- padding: 2px 6px;
517
- border-radius: 10px;
575
+ padding: 3px 9px;
576
+ border-radius: 15px;
518
577
  border: 1px solid #a5d8ff;
519
578
  white-space: nowrap;
520
- margin-bottom: 2px;
579
+ margin-bottom: 3px;
521
580
  }
522
581
  .no-alias {
523
- font-size: 11px;
582
+ font-size: 20px;
524
583
  color: #adb5bd;
525
584
  font-style: italic;
526
585
  }
527
586
  .status-container {
528
587
  display: flex;
529
588
  align-items: flex-end;
530
- margin-left: 8px;
589
+ margin-left: 12px;
531
590
  }
532
591
  .status {
533
592
  display: inline-flex;
534
593
  align-items: center;
535
- gap: 6px;
536
- padding: 3px 10px;
537
- border-radius: 16px;
538
- font-size: 11px;
539
- font-weight: 600;
594
+ gap: 9px;
595
+ padding: 4.5px 15px;
596
+ border-radius: 24px;
597
+ font-size: 20px;
598
+ font-weight: 900;
540
599
  white-space: nowrap;
541
600
  flex-shrink: 0;
542
601
  }
@@ -554,7 +613,7 @@ var SubscriptionService = class extends import_koishi.Service {
554
613
  text-align: center;
555
614
  color: #6c757d;
556
615
  font-style: italic;
557
- padding: 30px;
616
+ padding: 45px;
558
617
  background: #f8f9fa;
559
618
  grid-column: 1 / -1;
560
619
  }
@@ -564,7 +623,6 @@ var SubscriptionService = class extends import_koishi.Service {
564
623
  <div class="container">
565
624
  <div class="header">
566
625
  <h1>订阅状态总览</h1>
567
- <div class="subtitle">群组 ID: ${groupId}</div>
568
626
  </div>
569
627
 
570
628
  <div class="apps-container">
@@ -627,8 +685,20 @@ var SubscriptionService = class extends import_koishi.Service {
627
685
  if (subscriptions.length === 0) {
628
686
  return `群组 ${groupId} 暂无订阅`;
629
687
  }
630
- await ctx.database.remove("subscription_service", { groupId });
631
- return `已删除群组 ${groupId} 的所有订阅(共 ${subscriptions.length} 个)`;
688
+ const confirm = await session.send(
689
+ `确认要清空当前群聊的所有订阅吗?
690
+ 请输入 "是" 或 "确认" 或 "yes" 来确认删除,输入其他内容取消。`
691
+ );
692
+ const response = await session.prompt(3e4);
693
+ if (!response) {
694
+ return "操作已取消(超时)。";
695
+ }
696
+ if (["是", "确认", "yes", "y"].includes(response.toLowerCase())) {
697
+ await ctx.database.remove("subscription_service", { groupId });
698
+ return `已删除群组 ${groupId} 的所有订阅(共 ${subscriptions.length} 个)`;
699
+ } else {
700
+ return "操作已取消。";
701
+ }
632
702
  });
633
703
  ctx.command("subscription.stats", "查看订阅统计", {
634
704
  permissions: [`authority:${config.minAuthority}`],
package/lib/retry.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function retry<T>(retries: number, fn: () => Promise<T> | T, delay?: number): Promise<T>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-subscription",
3
3
  "description": "给多个应用提供订阅群组管理",
4
- "version": "0.0.4",
4
+ "version": "0.0.6",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [