koishi-plugin-chat-patch 5.3.0 → 5.3.1

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.
Files changed (35) hide show
  1. package/client/web/dist/assets/Chat-Bo5JdcNg.js +28 -0
  2. package/client/web/dist/assets/{Chat-CTdyeaX2.css → Chat-C-rmzMIw.css} +1 -1
  3. package/client/web/dist/assets/{MsgBody-DDacjWJ9.js → MsgBody-h4C1nnBQ.js} +1 -1
  4. package/client/web/dist/assets/{MsgBody.vue_vue_type_script_setup_true_lang-B1dWyCB0.js → MsgBody.vue_vue_type_script_setup_true_lang-a902ps2r.js} +2 -2
  5. package/client/web/dist/assets/{index-BlLut5xn.js → index-CdTmWcO4.js} +46 -46
  6. package/client/web/dist/assets/{index-fW_zcub3.css → index-wjp_A4n1.css} +1 -1
  7. package/client/web/dist/index.html +2 -2
  8. package/client/web/src/components/AboutPan.vue +2 -1
  9. package/client/web/src/components/BulletinBody.vue +1 -1
  10. package/client/web/src/components/FriendBody.vue +1 -0
  11. package/client/web/src/components/MsgBody.vue +1 -0
  12. package/client/web/src/components/TinySessionBody.vue +1 -0
  13. package/client/web/src/components/UpdatePan.vue +2 -2
  14. package/client/web/src/components/tooltip/UserInfoTooltip.vue +1 -1
  15. package/client/web/src/function/connect.ts +47 -14
  16. package/client/web/src/function/satori-model.ts +8 -0
  17. package/client/web/src/function/satori.ts +31 -1
  18. package/client/web/src/function/utils/avatarUtil.ts +18 -3
  19. package/client/web/src/main.ts +5 -0
  20. package/client/web/src/pages/Chat.vue +3 -4
  21. package/client/web/src/pages/Friends.vue +23 -17
  22. package/client/web/src/pages/Info.vue +3 -3
  23. package/client/web/src/pages/Messages.vue +20 -14
  24. package/client/web/src/pages/Qzone.vue +2 -2
  25. package/client/web/src/pages/chat-view/SystemNotice.vue +3 -3
  26. package/client/web/src/pages/options/OptAccount.vue +6 -4
  27. package/lib/index.js +14 -14
  28. package/lib/types.d.ts +115 -0
  29. package/package.json +1 -1
  30. package/src/bootstrap.ts +1 -0
  31. package/src/config.ts +1 -1
  32. package/src/recorder.ts +7 -7
  33. package/src/types.ts +4 -0
  34. package/src/web.ts +8 -9
  35. package/client/web/dist/assets/Chat-B448YOeN.js +0 -28
@@ -38,17 +38,17 @@
38
38
  </div>
39
39
  </div>
40
40
  <div id="message-list-body" class="robot-accordion-list">
41
- <div v-for="bot in satoriLogins" :key="'bot-' + bot.selfId"
42
- :class="['robot-accordion', { expanded: bot.selfId === activeBotId }]">
41
+ <div v-for="bot in satoriLogins" :key="'bot-' + botKey(bot.platform, bot.selfId)"
42
+ :class="['robot-accordion', { expanded: botKey(bot.platform, bot.selfId) === activeBotId }]">
43
43
  <div class="robot-accordion-header" @click="toggleRobot(bot)">
44
- <img :src="bot.avatar || '/img/icons/icon.svg'" :alt="bot.name" @error="avatarError">
44
+ <img data-avatar :src="bot.avatar || '/img/icons/icon.svg'" :alt="bot.name" @error="avatarError">
45
45
  <div>
46
46
  <span :title="bot.name">{{ bot.name }}</span>
47
47
  <small>{{ bot.platform }}</small>
48
48
  </div>
49
- <font-awesome-icon :icon="['fas', bot.selfId === activeBotId ? 'angle-up' : 'angle-down']" />
49
+ <font-awesome-icon :icon="['fas', botKey(bot.platform, bot.selfId) === activeBotId ? 'angle-up' : 'angle-down']" />
50
50
  </div>
51
- <div v-if="bot.selfId === activeBotId" class="robot-accordion-content">
51
+ <div v-if="botKey(bot.platform, bot.selfId) === activeBotId" class="robot-accordion-content">
52
52
  <div v-if="!showGroupAssist &&
53
53
  !contactStore.systemNoticesList &&
54
54
  (!contactStore.groupAssistList || contactStore.groupAssistList.length === 0) &&
@@ -218,7 +218,7 @@
218
218
  import { library } from '@fortawesome/fontawesome-svg-core'
219
219
  import { login as loginInfo } from '../function/connect'
220
220
  import { Connector, flushPendingBotEvents, loadContactsFromCache, restoreBotStateFromMessageCache } from '../function/connect'
221
- import { getActiveBot, getLogins, setActiveBot } from '../function/satori'
221
+ import { botKey, getActiveBot, getLogins, setActiveBot } from '../function/satori'
222
222
  import { getShowName, updateBaseOnMsgList } from '../function/utils/msgUtil'
223
223
 
224
224
  import {
@@ -249,7 +249,12 @@
249
249
  const chatStore = useChatStore()
250
250
  const settingsStore = useSettingsStore()
251
251
  const satoriLogins = ref<Array<{ platform: string; selfId: string; name: string; avatar?: string; status?: number; features?: string[] }>>(getLogins())
252
- const activeBotId = ref(getActiveBot()?.selfId ?? loginInfo.selectedSatoriBot ?? '')
252
+ const active = getActiveBot()
253
+ const activeBotId = ref(
254
+ active?.platform && active.selfId
255
+ ? botKey(active.platform, active.selfId)
256
+ : loginInfo.selectedSatoriBot ?? '',
257
+ )
253
258
 
254
259
  watch(() => loginInfo.satoriLogins, (list) => {
255
260
  satoriLogins.value = list ?? []
@@ -259,7 +264,7 @@
259
264
  }, { immediate: true })
260
265
 
261
266
  const currentBotName = computed(() => {
262
- return satoriLogins.value.find((item) => item.selfId === activeBotId.value)?.name ?? ''
267
+ return satoriLogins.value.find((item) => botKey(item.platform, item.selfId) === activeBotId.value)?.name ?? ''
263
268
  })
264
269
 
265
270
  function backToRobots() {
@@ -280,7 +285,7 @@
280
285
  }
281
286
 
282
287
  function restoreBotStateUi(bot: { platform: string; selfId: string }) {
283
- const restored = contactStore.botStates.get(bot.selfId)
288
+ const restored = contactStore.botStates.get(botKey(bot.platform, bot.selfId))
284
289
  if (!restored) return
285
290
  contactStore.baseOnMsgList.clear()
286
291
  for (const [, value] of restored.baseList) {
@@ -309,9 +314,10 @@
309
314
  }
310
315
 
311
316
  function selectRobot(bot: { platform: string; selfId: string; name: string; avatar?: string; features?: string[] }) {
312
- if (bot.selfId === activeBotId.value) return
317
+ const key = botKey(bot.platform, bot.selfId)
318
+ if (key === activeBotId.value) return
313
319
  snapshotCurrentBot()
314
- activeBotId.value = bot.selfId
320
+ activeBotId.value = key
315
321
  setActiveBot(bot.platform, bot.selfId)
316
322
  loginInfo.uin = bot.selfId
317
323
  loginInfo.nickname = bot.name
@@ -324,9 +330,9 @@
324
330
  platform: bot.platform,
325
331
  avatar: bot.avatar,
326
332
  satoriLogins: satoriLogins.value,
327
- selectedSatoriBot: bot.selfId,
333
+ selectedSatoriBot: key,
328
334
  }
329
- const saved = contactStore.botStates.get(bot.selfId)
335
+ const saved = contactStore.botStates.get(key)
330
336
  if (saved) {
331
337
  contactStore.userList = saved.userList
332
338
  contactStore.baseOnMsgList.clear()
@@ -354,7 +360,7 @@
354
360
  }
355
361
 
356
362
  function toggleRobot(bot: { platform: string; selfId: string; name: string; avatar?: string; features?: string[] }) {
357
- if (activeBotId.value === bot.selfId) {
363
+ if (activeBotId.value === botKey(bot.platform, bot.selfId)) {
358
364
  backToRobots()
359
365
  } else {
360
366
  selectRobot(bot)
@@ -65,7 +65,7 @@
65
65
  class="qzone-feed-card ss-card">
66
66
  <article>
67
67
  <header class="qzone-feed-card-header">
68
- <img :src="item.avatar" :alt="item.nickname">
68
+ <img data-avatar :src="item.avatar" :alt="item.nickname">
69
69
  <div class="qzone-feed-meta">
70
70
  <a>{{ item.nickname }}</a>
71
71
  <span>{{ item.timeText }}</span>
@@ -149,7 +149,7 @@
149
149
  </template>
150
150
  </div>
151
151
  <div class="qzone-feed-reply">
152
- <img :src="authStore.loginInfo.avatar || '/img/icons/icon.svg'">
152
+ <img data-avatar :src="authStore.loginInfo.avatar || '/img/icons/icon.svg'">
153
153
  <label :for="`qzone-reply-input-${item.id}`" class="sr-only">{{ $t('评论输入框') }}</label>
154
154
  <input :id="`qzone-reply-input-${item.id}`"
155
155
  v-model="getReplyDraft(item.id).content"
@@ -20,7 +20,7 @@
20
20
  :key="'sysNot-' + index">
21
21
  <div v-if="notice.request_type == 'friend'">
22
22
  <div>
23
- <img :src="notice.avatar || '/img/icons/icon.svg'">
23
+ <img data-avatar :src="notice.avatar || '/img/icons/icon.svg'">
24
24
  <div>
25
25
  <span>{{ notice.user_id }}
26
26
  {{ $t('请求加为好友') }}</span>
@@ -49,7 +49,7 @@
49
49
  </div>
50
50
  <div v-else-if="notice.request_type == 'group'">
51
51
  <div v-if="notice.sub_type == 'add'">
52
- <img :src="notice.avatar || '/img/icons/icon.svg'">
52
+ <img data-avatar :src="notice.avatar || '/img/icons/icon.svg'">
53
53
  <div>
54
54
  <span>{{ getName(notice.user_id) }}
55
55
  {{ $t('申请加入群聊') }}
@@ -68,7 +68,7 @@
68
68
  </div>
69
69
  <div v-else>
70
70
  <!-- TODO:这情况会出现在 notice 里?记不太清了,先放着吧 😭 -->
71
- <img :src="notice.group_avatar || '/img/icons/icon.svg'">
71
+ <img data-avatar :src="notice.group_avatar || '/img/icons/icon.svg'">
72
72
  <div>
73
73
  <span>{{ getName(notice.user_id) }}
74
74
  {{ $t('邀请你加入群聊') }}
@@ -13,7 +13,7 @@
13
13
  <div v-for="account in accounts"
14
14
  :key="account.platform + ':' + account.selfId"
15
15
  :class="['ss-card', 'account-info', { active: isActive(account) }]">
16
- <img :src="account.avatar || '/img/icons/icon.svg'">
16
+ <img data-avatar :src="account.avatar || '/img/icons/icon.svg'">
17
17
  <div>
18
18
  <div>
19
19
  <span>{{ account.name || account.selfId }}</span>
@@ -38,7 +38,7 @@
38
38
  <script setup lang="ts">
39
39
  import { computed } from 'vue'
40
40
  import { login as loginInfo } from '../../function/connect'
41
- import { getActiveBot } from '../../function/satori'
41
+ import { botKey, getActiveBot, getLogins } from '../../function/satori'
42
42
  import { useAuthStore } from '../../state/auth'
43
43
  import { i18n } from '../../main'
44
44
 
@@ -63,9 +63,11 @@ const accounts = computed<SatoriAccount[]>(() => {
63
63
  const activeBot = computed(() => {
64
64
  const current = getActiveBot()
65
65
  if (current?.platform && current.selfId) return current
66
+ const selectedKey = String(authStore.loginInfo.selectedSatoriBot ?? '')
67
+ const selected = getLogins().find((item) => botKey(item.platform, item.selfId) === selectedKey)
66
68
  return {
67
- platform: String(authStore.loginInfo.platform ?? ''),
68
- selfId: String(authStore.loginInfo.uin ?? authStore.loginInfo.selectedSatoriBot ?? ''),
69
+ platform: String(selected?.platform ?? authStore.loginInfo.platform ?? ''),
70
+ selfId: String(selected?.selfId ?? authStore.loginInfo.uin ?? ''),
69
71
  }
70
72
  })
71
73
 
package/lib/index.js CHANGED
@@ -844,8 +844,8 @@ var _Recorder = class _Recorder {
844
844
  }
845
845
  async handleSession(session) {
846
846
  const platform = session.platform || "unknown";
847
- if (this.isBlocked(platform)) return;
848
847
  if (!MESSAGE_TYPES.has(session.type)) return;
848
+ if (this.isBlocked(platform)) return;
849
849
  const event = session.toJSON();
850
850
  const message = event.message;
851
851
  const record = {
@@ -866,15 +866,15 @@ var _Recorder = class _Recorder {
866
866
  };
867
867
  try {
868
868
  await this.database.appendMessage(record);
869
- void this.cacheMessageContacts(event).catch((error) => {
870
- this.logger.warn("缓存消息联系人失败:", error);
871
- });
872
- void this.cacheMessageMedia(event).catch((error) => {
873
- this.logger.warn("异步缓存消息媒体失败:", error);
874
- });
875
869
  } catch (error) {
876
870
  this.logger.warn("写入历史消息失败:", error);
877
871
  }
872
+ void this.cacheMessageContacts(event).catch((error) => {
873
+ this.logger.warn("缓存消息联系人失败:", error);
874
+ });
875
+ void this.cacheMessageMedia(event).catch((error) => {
876
+ this.logger.warn("异步缓存消息媒体失败:", error);
877
+ });
878
878
  }
879
879
  async cacheMessageContacts(event) {
880
880
  const platform = event.platform || "";
@@ -1622,7 +1622,8 @@ function registerBootstrap(ctx, config, database, logger) {
1622
1622
  return {
1623
1623
  endpoint: resolveEndpoint(ctx, config),
1624
1624
  token: ctx.satori?.server?.config?.token ?? "",
1625
- basePath: config.basePath
1625
+ basePath: config.basePath,
1626
+ blockedPlatforms: config.blockedPlatforms ?? []
1626
1627
  };
1627
1628
  }, { authority: 4 });
1628
1629
  ctx.console.addListener("chat-patch/history", async (query) => {
@@ -1706,13 +1707,12 @@ function registerWeb(ctx, config, database, contactCache, logger) {
1706
1707
  koa.status = 404;
1707
1708
  koa.body = "";
1708
1709
  }, "servePublicFile");
1709
- const registerDevPublic = /* @__PURE__ */ __name((pattern, prefix) => {
1710
- ctx.server.get(pattern, async (koa, next) => {
1711
- if (!getVite()) return next();
1710
+ const registerPublic = /* @__PURE__ */ __name((pattern, prefix) => {
1711
+ ctx.server.get(pattern, async (koa) => {
1712
1712
  await servePublicFile(koa, prefix);
1713
1713
  });
1714
- }, "registerDevPublic");
1715
- registerDevPublic("/img(.*)", "img");
1714
+ }, "registerPublic");
1715
+ registerPublic("/img(.*)", "img");
1716
1716
  const toExtension2 = /* @__PURE__ */ __name((value) => {
1717
1717
  const ext = value.startsWith(".") ? value : `.${value}`;
1718
1718
  return ext.toLowerCase();
@@ -2283,7 +2283,7 @@ var Config = import_koishi2.Schema.intersect([
2283
2283
  blockedPlatforms: import_koishi2.Schema.array(import_koishi2.Schema.object({
2284
2284
  platformName: import_koishi2.Schema.string(),
2285
2285
  exactMatch: import_koishi2.Schema.boolean().default(true)
2286
- })).role("table").description("不记录历史的平台").default([
2286
+ })).role("table").description("屏蔽消息接收的平台").default([
2287
2287
  { platformName: "qq", exactMatch: true },
2288
2288
  { platformName: "qqguild", exactMatch: true },
2289
2289
  { platformName: "sandbox", exactMatch: false }
package/lib/types.d.ts ADDED
@@ -0,0 +1,115 @@
1
+ import type { Awaitable } from 'koishi';
2
+ export interface SatoriBootstrap {
3
+ endpoint: string;
4
+ token: string;
5
+ basePath: string;
6
+ blockedPlatforms: Array<{
7
+ platformName: string;
8
+ exactMatch: boolean;
9
+ }>;
10
+ }
11
+ export interface MessageRecord {
12
+ id?: string;
13
+ sequence?: number;
14
+ type: string;
15
+ platform: string;
16
+ selfId: string;
17
+ channelId?: string;
18
+ guildId?: string;
19
+ userId?: string;
20
+ timestamp: number;
21
+ timestampMs?: number;
22
+ receivedAt?: number;
23
+ content?: string;
24
+ elements?: unknown[];
25
+ raw?: unknown;
26
+ }
27
+ export type SelfMessageChannelType = 'group' | 'user';
28
+ export type SelfMessageSource = 'webui' | 'bot' | 'plugin';
29
+ export interface SelfMessageRecord {
30
+ id: string;
31
+ platform: string;
32
+ selfId: string;
33
+ channelId: string;
34
+ guildId?: string;
35
+ channelType?: SelfMessageChannelType;
36
+ messageId?: string;
37
+ content?: string;
38
+ elements?: unknown[];
39
+ message?: unknown[];
40
+ forwardId?: string;
41
+ forwardContent?: unknown[];
42
+ sentAt: number;
43
+ sequence: number;
44
+ source: SelfMessageSource;
45
+ kind: string;
46
+ fingerprint?: string;
47
+ }
48
+ export interface SelfMessagePayload {
49
+ id?: string;
50
+ platform: string;
51
+ selfId: string;
52
+ channelId: string;
53
+ guildId?: string;
54
+ channelType?: SelfMessageChannelType;
55
+ messageId?: string;
56
+ content?: string;
57
+ elements?: unknown[];
58
+ message?: unknown[];
59
+ forwardId?: string;
60
+ forwardContent?: unknown[];
61
+ sentAt?: number;
62
+ sequence?: number;
63
+ source?: SelfMessageSource;
64
+ kind?: string;
65
+ }
66
+ export interface HistoryQuery {
67
+ platform: string;
68
+ selfId: string;
69
+ channelId: string;
70
+ limit?: number;
71
+ }
72
+ export interface HistoryResult {
73
+ messages: MessageRecord[];
74
+ }
75
+ export interface PinnedState {
76
+ bots: string[];
77
+ channels: string[];
78
+ }
79
+ export interface PluginConfigPayload {
80
+ basePath: string;
81
+ maxMessagesPerChannel: number;
82
+ historyPageSize: number;
83
+ maxMediaFiles: number;
84
+ blockedPlatforms: Array<{
85
+ platformName: string;
86
+ exactMatch: boolean;
87
+ }>;
88
+ loggerinfo: boolean;
89
+ }
90
+ export interface ContactCacheItem {
91
+ id: string;
92
+ name: string;
93
+ avatar?: string;
94
+ channelId?: string;
95
+ guildId?: string;
96
+ raw?: unknown;
97
+ }
98
+ export interface ContactCacheQuery {
99
+ platform: string;
100
+ selfId: string;
101
+ type: string;
102
+ contacts?: ContactCacheItem[];
103
+ append?: boolean;
104
+ }
105
+ export interface ContactCacheResult {
106
+ contacts?: ContactCacheItem[];
107
+ }
108
+ declare module '@koishijs/console' {
109
+ interface Events {
110
+ 'chat-patch/bootstrap'(): SatoriBootstrap;
111
+ 'chat-patch/history'(query: HistoryQuery): Awaitable<HistoryResult>;
112
+ 'chat-patch/config'(): PluginConfigPayload;
113
+ 'chat-patch/contact-cache'(query: ContactCacheQuery): Awaitable<ContactCacheResult>;
114
+ }
115
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chat-patch",
3
3
  "description": "[<ruby>chat-patch<rp>(</rp><rt>点我预览效果</rt><rp>)</rp></ruby>](https://i0.hdslb.com/bfs/openplatform/71074dfc9e5256fc3333d8bd8478bec1af874046.png) 视奸小插件((bushi( (低性能警告)。手机端适配。灵感来自 chat 插件。",
4
- "version": "5.3.0",
4
+ "version": "5.3.1",
5
5
  "scripts": {
6
6
  "build:web": "npm --prefix client/web run build",
7
7
  "prepack": "npm run build:web",
package/src/bootstrap.ts CHANGED
@@ -31,6 +31,7 @@ export function registerBootstrap(
31
31
  endpoint: resolveEndpoint(ctx, config),
32
32
  token: ctx.satori?.server?.config?.token ?? '',
33
33
  basePath: config.basePath,
34
+ blockedPlatforms: config.blockedPlatforms ?? [],
34
35
  }
35
36
  }, { authority: 4 })
36
37
 
package/src/config.ts CHANGED
@@ -25,7 +25,7 @@ export const Config: Schema<Config> = Schema.intersect([
25
25
  blockedPlatforms: Schema.array(Schema.object({
26
26
  platformName: Schema.string(),
27
27
  exactMatch: Schema.boolean().default(true),
28
- })).role('table').description('不记录历史的平台').default([
28
+ })).role('table').description('屏蔽消息接收的平台').default([
29
29
  { platformName: 'qq', exactMatch: true },
30
30
  { platformName: 'qqguild', exactMatch: true },
31
31
  { platformName: 'sandbox', exactMatch: false },
package/src/recorder.ts CHANGED
@@ -41,8 +41,8 @@ export class Recorder {
41
41
 
42
42
  private async handleSession(session: Session) {
43
43
  const platform = session.platform || 'unknown'
44
- if (this.isBlocked(platform)) return
45
44
  if (!MESSAGE_TYPES.has(session.type)) return
45
+ if (this.isBlocked(platform)) return
46
46
 
47
47
  const event = session.toJSON()
48
48
  const message = event.message
@@ -65,15 +65,15 @@ export class Recorder {
65
65
 
66
66
  try {
67
67
  await this.database.appendMessage(record)
68
- void this.cacheMessageContacts(event).catch((error) => {
69
- this.logger.warn('缓存消息联系人失败:', error)
70
- })
71
- void this.cacheMessageMedia(event).catch((error) => {
72
- this.logger.warn('异步缓存消息媒体失败:', error)
73
- })
74
68
  } catch (error) {
75
69
  this.logger.warn('写入历史消息失败:', error)
76
70
  }
71
+ void this.cacheMessageContacts(event).catch((error) => {
72
+ this.logger.warn('缓存消息联系人失败:', error)
73
+ })
74
+ void this.cacheMessageMedia(event).catch((error) => {
75
+ this.logger.warn('异步缓存消息媒体失败:', error)
76
+ })
77
77
  }
78
78
 
79
79
  private async cacheMessageContacts(event: ReturnType<Session['toJSON']>) {
package/src/types.ts CHANGED
@@ -5,6 +5,10 @@ export interface SatoriBootstrap {
5
5
  endpoint: string
6
6
  token: string
7
7
  basePath: string
8
+ blockedPlatforms: Array<{
9
+ platformName: string
10
+ exactMatch: boolean
11
+ }>
8
12
  }
9
13
 
10
14
  export interface MessageRecord {
package/src/web.ts CHANGED
@@ -82,15 +82,14 @@ export function registerWeb(
82
82
  koa.body = ''
83
83
  }
84
84
 
85
- // web 应用里仍有 /img 这类运行时绝对路径,开发模式直接由 Koishi 服务托管
86
- const registerDevPublic = (pattern: string, prefix: string) => {
87
- ctx.server.get(pattern, async (koa: WebContext, next: () => Promise<void>) => {
88
- if (!getVite()) return next()
89
- await servePublicFile(koa, prefix)
90
- })
91
- }
92
-
93
- registerDevPublic('/img(.*)', 'img')
85
+ // web 应用里仍有 /img 这类运行时绝对路径,生产/开发模式都要由 Koishi 服务托管
86
+ const registerPublic = (pattern: string, prefix: string) => {
87
+ ctx.server.get(pattern, async (koa: WebContext) => {
88
+ await servePublicFile(koa, prefix)
89
+ })
90
+ }
91
+
92
+ registerPublic('/img(.*)', 'img')
94
93
 
95
94
  const toExtension = (value: string): string => {
96
95
  const ext = value.startsWith('.') ? value : `.${value}`