koishi-plugin-chat-patch 3.0.2 → 5.0.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 (237) hide show
  1. package/client/vue/index.vue +60 -634
  2. package/client/web/dist/assets/AppIcon-DIku6-h0.png +0 -0
  3. package/client/web/dist/assets/AppIconDark-BYymN71t.png +0 -0
  4. package/client/web/dist/assets/CapacitorAppIcon-CSZwNhvi.png +0 -0
  5. package/client/web/dist/assets/CapacitorAppIconDark-BMvMOzsg.png +0 -0
  6. package/client/web/dist/assets/Chat-CTdyeaX2.css +1 -0
  7. package/client/web/dist/assets/Chat-DkZvD7d8.js +28 -0
  8. package/client/web/dist/assets/MealHungryPan-Beos3lRI.js +1 -0
  9. package/client/web/dist/assets/MsgBody-aZ4_aija.js +1 -0
  10. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-CPZjRLp6.js +67 -0
  11. package/client/web/dist/assets/NapcatAppIcon-C7VOAkW9.png +0 -0
  12. package/client/web/dist/assets/Vue-lEhHouxB.png +0 -0
  13. package/client/web/dist/assets/append_darwin-CqBNU62J.css +1 -0
  14. package/client/web/dist/assets/append_full_vibrancy-j0D2LM9m.css +1 -0
  15. package/client/web/dist/assets/append_linux-uhxeBqeC.css +1 -0
  16. package/client/web/dist/assets/append_linux_vibrancy-COiZq8MS.css +1 -0
  17. package/client/web/dist/assets/append_napcat-DV8CAyAd.css +1 -0
  18. package/client/web/dist/assets/append_new-407Lo5_V.css +1 -0
  19. package/client/web/dist/assets/append_vibrancy-6FHctkOY.css +1 -0
  20. package/client/web/dist/assets/append_win32-srmD-78y.css +1 -0
  21. package/client/web/dist/assets/cakes-CxTVKdnK.svg +189 -0
  22. package/client/web/dist/assets/core-DhEqZVGG.js +1 -0
  23. package/client/web/dist/assets/event-Cadp6m6J.js +1 -0
  24. package/client/web/dist/assets/index-CPTXiemn.js +4045 -0
  25. package/client/web/dist/assets/index-Cg_esKB0.js +1 -0
  26. package/client/web/dist/assets/index-Cln012sP.css +1 -0
  27. package/client/web/dist/assets//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223-CJKIKBn3.ttf +0 -0
  28. package/client/web/dist/assets//346/234/237/345/276/205-C90_OY60.png +0 -0
  29. package/client/web/dist/bcui/css/color-dark.css +35 -0
  30. package/client/web/dist/bcui/css/color-light.css +35 -0
  31. package/client/web/dist/bcui/css/style.css +468 -0
  32. package/client/web/dist/bcui/js/main.js +193 -0
  33. package/client/web/dist/css/append-dark.css +13 -0
  34. package/client/web/dist/css/append-light.css +13 -0
  35. package/client/web/dist/css/append-napcat-dark.css +14 -0
  36. package/client/web/dist/css/append-napcat-light.css +14 -0
  37. package/client/web/dist/favicon.ico +0 -0
  38. package/client/web/dist/img/icons/icon-for-fuck-apple.png +0 -0
  39. package/client/web/dist/img/icons/icon-maskable.png +0 -0
  40. package/client/web/dist/img/icons/icon.png +0 -0
  41. package/client/web/dist/img/icons/icon.svg +10 -0
  42. package/client/web/dist/index.html +22 -0
  43. package/client/web/dist/robots.txt +2 -0
  44. package/client/web/dist/sw.js +58 -0
  45. package/client/web/env.d.ts +56 -0
  46. package/client/web/index.html +21 -0
  47. package/client/web/package-lock.json +3419 -0
  48. package/client/web/package.json +62 -0
  49. package/client/web/public/bcui/css/color-dark.css +35 -0
  50. package/client/web/public/bcui/css/color-light.css +35 -0
  51. package/client/web/public/bcui/css/style.css +468 -0
  52. package/client/web/public/bcui/js/main.js +193 -0
  53. package/client/web/public/css/append-dark.css +13 -0
  54. package/client/web/public/css/append-light.css +13 -0
  55. package/client/web/public/css/append-napcat-dark.css +14 -0
  56. package/client/web/public/css/append-napcat-light.css +14 -0
  57. package/client/web/public/favicon.ico +0 -0
  58. package/client/web/public/img/icons/icon-for-fuck-apple.png +0 -0
  59. package/client/web/public/img/icons/icon-maskable.png +0 -0
  60. package/client/web/public/img/icons/icon.png +0 -0
  61. package/client/web/public/img/icons/icon.svg +10 -0
  62. package/client/web/public/robots.txt +2 -0
  63. package/client/web/public/sw.js +58 -0
  64. package/client/web/scripts/copy-bcui.mjs +17 -0
  65. package/client/web/src/App.vue +1127 -0
  66. package/client/web/src/assets/css/append/append_darwin.css +68 -0
  67. package/client/web/src/assets/css/append/append_full_vibrancy.css +290 -0
  68. package/client/web/src/assets/css/append/append_linux.css +125 -0
  69. package/client/web/src/assets/css/append/append_linux_vibrancy.css +22 -0
  70. package/client/web/src/assets/css/append/append_napcat.css +35 -0
  71. package/client/web/src/assets/css/append/append_new.css +843 -0
  72. package/client/web/src/assets/css/append/append_vibrancy.css +113 -0
  73. package/client/web/src/assets/css/append/append_win32.css +109 -0
  74. package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +349 -0
  75. package/client/web/src/assets/css/append/mobile/append_mobile_vertical.css +96 -0
  76. package/client/web/src/assets/css/chat.css +2104 -0
  77. package/client/web/src/assets/css/msg.css +941 -0
  78. package/client/web/src/assets/css/options.css +811 -0
  79. package/client/web/src/assets/css/sys_notice.css +62 -0
  80. package/client/web/src/assets/css/view.css +2105 -0
  81. package/client/web/src/assets/font//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223.ttf +0 -0
  82. package/client/web/src/assets/img/Vue.png +0 -0
  83. package/client/web/src/assets/img/cakes.svg +189 -0
  84. package/client/web/src/assets/img/icons/AppIcon.png +0 -0
  85. package/client/web/src/assets/img/icons/AppIconDark.png +0 -0
  86. package/client/web/src/assets/img/icons/CapacitorAppIcon.png +0 -0
  87. package/client/web/src/assets/img/icons/CapacitorAppIconDark.png +0 -0
  88. package/client/web/src/assets/img/icons/NapcatAppIcon.png +0 -0
  89. package/client/web/src/assets/img/icons/NapcatAppIconDark.png +0 -0
  90. package/client/web/src/assets/img/icons/exit.png +0 -0
  91. package/client/web/src/assets/img/icons/pen.png +0 -0
  92. package/client/web/src/assets/img/stars.svg +89 -0
  93. package/client/web/src/assets/img//346/234/237/345/276/205.png +0 -0
  94. package/client/web/src/assets/l10n/_About I10n.md +35 -0
  95. package/client/web/src/assets/l10n/_l10nconfig.json +7 -0
  96. package/client/web/src/assets/l10n/zh-CN.po +1941 -0
  97. package/client/web/src/assets/logo.png +0 -0
  98. package/client/web/src/assets/pathMap/LLOneBot.yaml +2 -0
  99. package/client/web/src/assets/pathMap/Lagrange.OneBot.yaml +180 -0
  100. package/client/web/src/assets/pathMap/NapCat.Onebot.yaml +224 -0
  101. package/client/web/src/assets/pathMap/SnowLuma.yaml +80 -0
  102. package/client/web/src/assets/qed.txt +1 -0
  103. package/client/web/src/components/AboutPan.vue +197 -0
  104. package/client/web/src/components/BulletinBody.vue +129 -0
  105. package/client/web/src/components/DepPan.vue +310 -0
  106. package/client/web/src/components/EmojiFace.vue +32 -0
  107. package/client/web/src/components/FacePan.vue +461 -0
  108. package/client/web/src/components/FileBody.vue +129 -0
  109. package/client/web/src/components/FileManager.vue +532 -0
  110. package/client/web/src/components/FriendBody.vue +54 -0
  111. package/client/web/src/components/GlobalSessionSearchBar.vue +301 -0
  112. package/client/web/src/components/History.vue +49 -0
  113. package/client/web/src/components/LazyLottie.vue +82 -0
  114. package/client/web/src/components/MergePan.vue +127 -0
  115. package/client/web/src/components/MsgBody.vue +1478 -0
  116. package/client/web/src/components/MusicPlayer.vue +549 -0
  117. package/client/web/src/components/NoticeBody.vue +168 -0
  118. package/client/web/src/components/QzonePermissionPan.vue +150 -0
  119. package/client/web/src/components/RawMsgRenderPreviewPan.vue +190 -0
  120. package/client/web/src/components/ThemeColorPickerPan.vue +420 -0
  121. package/client/web/src/components/TinySessionBody.vue +51 -0
  122. package/client/web/src/components/UmamiInfoPan.vue +1549 -0
  123. package/client/web/src/components/UpdatePan.vue +263 -0
  124. package/client/web/src/components/ViewerCom.vue +1360 -0
  125. package/client/web/src/components/VoiceMsg.vue +626 -0
  126. package/client/web/src/components/WelPan.vue +894 -0
  127. package/client/web/src/components/msg-body/.gitkeep +0 -0
  128. package/client/web/src/components/msg-component/JsonSegComp.vue +138 -0
  129. package/client/web/src/components/msg-component/XmlSegComp.vue +132 -0
  130. package/client/web/src/components/msg-component/jsonComp/AutoReply.vue +97 -0
  131. package/client/web/src/components/msg-component/jsonComp/Contact.lua.vue +129 -0
  132. package/client/web/src/components/msg-component/jsonComp/Feed.lua.vue +94 -0
  133. package/client/web/src/components/msg-component/jsonComp/Forum.vue +84 -0
  134. package/client/web/src/components/msg-component/jsonComp/Mannounce.vue +111 -0
  135. package/client/web/src/components/msg-component/jsonComp/Map.vue +52 -0
  136. package/client/web/src/components/msg-component/jsonComp/Miniapp.lua.vue +53 -0
  137. package/client/web/src/components/msg-component/jsonComp/Miniapp.vue +69 -0
  138. package/client/web/src/components/msg-component/jsonComp/Music.lua.vue +135 -0
  139. package/client/web/src/components/msg-component/jsonComp/Tuwen.lua.vue +56 -0
  140. package/client/web/src/components/notice-component/.gitkeep +0 -0
  141. package/client/web/src/components/notice-component/MealHungryPan.vue +119 -0
  142. package/client/web/src/components/tooltip/CustomFaceTooltip.vue +22 -0
  143. package/client/web/src/components/tooltip/Tooltip.vue +50 -0
  144. package/client/web/src/components/tooltip/Tooltips.vue +14 -0
  145. package/client/web/src/components/tooltip/UserInfoTooltip.vue +69 -0
  146. package/client/web/src/function/base.ts +238 -0
  147. package/client/web/src/function/connect.ts +2042 -0
  148. package/client/web/src/function/debug.ts +10 -0
  149. package/client/web/src/function/elements/information.ts +141 -0
  150. package/client/web/src/function/elements/system.ts +101 -0
  151. package/client/web/src/function/elements/vueComp.ts +62 -0
  152. package/client/web/src/function/favicon.ts +76 -0
  153. package/client/web/src/function/input.ts +69 -0
  154. package/client/web/src/function/model/emoji.ts +127 -0
  155. package/client/web/src/function/model/img.ts +150 -0
  156. package/client/web/src/function/model/msg-body.ts +51 -0
  157. package/client/web/src/function/msg.ts +2674 -0
  158. package/client/web/src/function/notify.ts +225 -0
  159. package/client/web/src/function/option.ts +749 -0
  160. package/client/web/src/function/satori-model.ts +818 -0
  161. package/client/web/src/function/satori.ts +325 -0
  162. package/client/web/src/function/sender.ts +178 -0
  163. package/client/web/src/function/tooltip.ts +49 -0
  164. package/client/web/src/function/utils/appUtil.ts +2288 -0
  165. package/client/web/src/function/utils/avatarUtil.ts +31 -0
  166. package/client/web/src/function/utils/backgroundUtil.ts +110 -0
  167. package/client/web/src/function/utils/linkViewUtil.ts +101 -0
  168. package/client/web/src/function/utils/localHistoryUtil.ts +517 -0
  169. package/client/web/src/function/utils/msgUtil.ts +1242 -0
  170. package/client/web/src/function/utils/pinyin.ts +141 -0
  171. package/client/web/src/function/utils/recordUtil.ts +187 -0
  172. package/client/web/src/function/utils/sessionUtil.ts +114 -0
  173. package/client/web/src/function/utils/systemUtil.ts +664 -0
  174. package/client/web/src/main.ts +91 -0
  175. package/client/web/src/pages/Chat.vue +3453 -0
  176. package/client/web/src/pages/Friends.vue +636 -0
  177. package/client/web/src/pages/Info.vue +717 -0
  178. package/client/web/src/pages/Messages.vue +875 -0
  179. package/client/web/src/pages/Options.vue +82 -0
  180. package/client/web/src/pages/Qzone.vue +1178 -0
  181. package/client/web/src/pages/chat-view/ChatDanmaku.vue +759 -0
  182. package/client/web/src/pages/chat-view/ChatGlagame.vue +642 -0
  183. package/client/web/src/pages/chat-view/ChatTerminal.vue +1044 -0
  184. package/client/web/src/pages/chat-view/SystemNotice.vue +171 -0
  185. package/client/web/src/pages/options/OptAccount.vue +107 -0
  186. package/client/web/src/pages/options/OptAddon.vue +185 -0
  187. package/client/web/src/pages/options/OptDev.vue +704 -0
  188. package/client/web/src/pages/options/OptFunction.vue +403 -0
  189. package/client/web/src/pages/options/OptInfo.vue +165 -0
  190. package/client/web/src/pages/options/OptView.vue +846 -0
  191. package/client/web/src/runtime/backend.ts +310 -0
  192. package/client/web/src/state/auth.ts +14 -0
  193. package/client/web/src/state/chat.ts +36 -0
  194. package/client/web/src/state/connection.ts +20 -0
  195. package/client/web/src/state/contact.ts +37 -0
  196. package/client/web/src/state/qzone.ts +18 -0
  197. package/client/web/src/state/sessionHistory.ts +25 -0
  198. package/client/web/src/state/settings.ts +18 -0
  199. package/client/web/src/state/sticker.ts +10 -0
  200. package/client/web/src/state/ui.ts +56 -0
  201. package/client/web/tsconfig.json +27 -0
  202. package/client/web/tsconfig.tsbuildinfo +1 -0
  203. package/client/web/vite.config.ts +33 -0
  204. package/dist/index.js +1 -12
  205. package/dist/style.css +1 -1
  206. package/lib/index.js +2070 -2077
  207. package/package.json +15 -2
  208. package/readme.md +0 -22
  209. package/src/bootstrap.ts +71 -0
  210. package/src/cache.ts +319 -0
  211. package/src/config.ts +23 -35
  212. package/src/database.ts +650 -0
  213. package/src/index.ts +27 -59
  214. package/src/media.ts +302 -0
  215. package/src/recorder.ts +175 -0
  216. package/src/self-message.ts +456 -0
  217. package/src/server.d.ts +26 -0
  218. package/src/types.ts +108 -48
  219. package/src/web.ts +712 -0
  220. package/client/vue/chat-logic.ts +0 -683
  221. package/client/vue/composables/useChatActions.ts +0 -61
  222. package/client/vue/composables/useChatData.ts +0 -185
  223. package/client/vue/composables/useImageCache.ts +0 -281
  224. package/client/vue/composables/useVideoCache.ts +0 -125
  225. package/client/vue/types.ts +0 -72
  226. package/lib/api-handlers.d.ts +0 -25
  227. package/lib/config.d.ts +0 -14
  228. package/lib/file-manager.d.ts +0 -124
  229. package/lib/index.d.ts +0 -17
  230. package/lib/logger.d.ts +0 -9
  231. package/lib/message-handler.d.ts +0 -35
  232. package/lib/types.d.ts +0 -57
  233. package/lib/utils.d.ts +0 -18
  234. package/src/api-handlers.ts +0 -646
  235. package/src/file-manager.ts +0 -1436
  236. package/src/message-handler.ts +0 -507
  237. package/src/utils.ts +0 -189
File without changes
@@ -0,0 +1,138 @@
1
+ <!--
2
+ * @FileDescription: 卡片消息消息组件
3
+ * @Author: Stapxs
4
+ * @Date: 2023/05/23
5
+ * 2026/02/13
6
+ * @Version: 1.0 - 初始版本
7
+ * 2.0 - 重构为单独组件
8
+ -->
9
+
10
+ <template>
11
+ <template v-if="comp">
12
+ <component :is="comp" :id="id" :data="data" />
13
+ </template>
14
+ <div v-else-if="genericCard" class="msg-json generic-card" @click="openGenericCard(genericCard.url)">
15
+ <img v-if="genericCard.img" :src="genericCard.img" alt="">
16
+ <div>
17
+ <p>{{ genericCard.title }}</p>
18
+ <span v-if="genericCard.desc">{{ genericCard.desc }}</span>
19
+ <small v-if="genericCard.tag">{{ genericCard.tag }}</small>
20
+ </div>
21
+ </div>
22
+ <span v-else class="msg-unknown">{{
23
+ '( ' + $t('不支持的卡片类型') + ': ' + id + ' )'
24
+ }}</span>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { computed } from 'vue'
29
+ import { openLink } from '../../function/utils/appUtil'
30
+
31
+ const comps = import.meta.glob('./jsonComp/*.vue', {
32
+ eager: true,
33
+ import: 'default',
34
+ })
35
+
36
+ const cardComponentMap = {
37
+ 'com.tencent.tuwen.lua': comps['./jsonComp/Tuwen.lua.vue'],
38
+ 'com.tencent.mannounce': comps['./jsonComp/Mannounce.vue'],
39
+ 'com.tencent.miniapp.lua': comps['./jsonComp/Miniapp.lua.vue'],
40
+ 'com.tencent.miniapp_01': comps['./jsonComp/Miniapp.vue'],
41
+ 'com.tencent.music.lua': comps['./jsonComp/Music.lua.vue'],
42
+ 'com.tencent.contact.lua': comps['./jsonComp/Contact.lua.vue'],
43
+ 'com.tencent.map': comps['./jsonComp/Map.vue'],
44
+ 'com.tencent.forum': comps['./jsonComp/Forum.vue'],
45
+ 'com.tencent.autoreply': comps['./jsonComp/AutoReply.vue'],
46
+ 'com.tencent.feed.lua': comps['./jsonComp/Feed.lua.vue'],
47
+ }
48
+
49
+ const { data } = defineProps<{
50
+ data: string,
51
+ }>()
52
+
53
+ let json: unknown
54
+ let id = ''
55
+
56
+ try {
57
+ json = JSON.parse(data)
58
+ if (json && typeof (json as any).app === 'string') {
59
+ id = (json as any).app
60
+ }
61
+ } catch {
62
+ json = null
63
+ id = ''
64
+ }
65
+
66
+ const comp = cardComponentMap[id]
67
+
68
+ function getString(value: unknown): string {
69
+ return typeof value === 'string' ? value : ''
70
+ }
71
+
72
+ const genericCard = computed(() => {
73
+ if (comp || !json || typeof json !== 'object' || Array.isArray(json)) return undefined
74
+ const root = json as Record<string, unknown>
75
+ const meta = root.meta
76
+ if (typeof meta !== 'object' || meta === null) return undefined
77
+ const metaList = Object.values(meta as Record<string, unknown>)
78
+ for (const value of metaList) {
79
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) continue
80
+ const card = value as Record<string, unknown>
81
+ const title = getString(card.title) || getString(card.name) || getString(card.nickname)
82
+ const desc = getString(card.desc)
83
+ || getString(card.summary)
84
+ || getString(card.content)
85
+ || getString(card.address)
86
+ const img = getString(card.preview)
87
+ || getString(card.avatar)
88
+ || getString(card.cover)
89
+ || getString(card.img)
90
+ if (!title && !desc && !img) continue
91
+ return {
92
+ title,
93
+ desc,
94
+ img,
95
+ tag: getString(card.tag) || getString(card.source) || getString(root.desc),
96
+ url: getString(card.jumpUrl) || getString(card.url) || getString(card.qqdocurl),
97
+ }
98
+ }
99
+ return undefined
100
+ })
101
+
102
+ function openGenericCard(url: string) {
103
+ if (url) openLink(url)
104
+ }
105
+ </script>
106
+
107
+ <style scoped>
108
+ .generic-card {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 12px;
112
+ cursor: pointer;
113
+ }
114
+ .generic-card img {
115
+ width: 48px;
116
+ height: 48px;
117
+ border-radius: 7px;
118
+ object-fit: cover;
119
+ flex-shrink: 0;
120
+ }
121
+ .generic-card > div {
122
+ min-width: 0;
123
+ }
124
+ .generic-card p {
125
+ margin: 0;
126
+ font-weight: bold;
127
+ }
128
+ .generic-card span,
129
+ .generic-card small {
130
+ display: block;
131
+ margin-top: 4px;
132
+ font-size: 0.8rem;
133
+ opacity: 0.7;
134
+ white-space: nowrap;
135
+ overflow: hidden;
136
+ text-overflow: ellipsis;
137
+ }
138
+ </style>
@@ -0,0 +1,132 @@
1
+ <!--
2
+ * @FileDescription: 卡片消息消息组件
3
+ * @Author: Mr.Lee
4
+ * @Date: 2026/02/13
5
+ * @Version: 1.0 - 从CardMsgComp拆分出来
6
+ -->
7
+
8
+ <template>
9
+ <div
10
+ ref="main"
11
+ @click="cardClick()"
12
+ v-html="buildXML(item, id, id)" />
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { Logger } from '../../function/base';
17
+ import { openLink } from '../../function/utils/appUtil'
18
+ import app from '../../main'
19
+ import { useTemplateRef } from 'vue'
20
+
21
+ const { item, id } = defineProps<{
22
+ item: string,
23
+ id: string,
24
+ }>()
25
+
26
+ const mainDom = useTemplateRef('main')
27
+
28
+ let link: string | undefined = undefined
29
+
30
+ /**
31
+ * 尝试解析渲染 XML 消息
32
+ * @param xml 原始的 XML 消息内容
33
+ * @param id XML 消息 ID(暂时不知道有什么用)
34
+ * @param msgid 消息 ID
35
+ * @returns 处理完成的 HTML 代码
36
+ */
37
+ function buildXML(xml: string, id: string, msgid: string) {
38
+ const $t = app.config.globalProperties.$t
39
+ try {
40
+ // <msg> 标签内的为本体
41
+ let item = xml.substring(xml.indexOf('<item'), xml.indexOf('</msg>'))
42
+ // 尝试转换标签为 html
43
+ // item = item.replaceAll('/>', '>')
44
+ item = item.replaceAll('item', 'div') // item
45
+ item = item.replaceAll('<div', '<div class="msg-xml"')
46
+ item = item.replaceAll('title', 'p') // title
47
+ item = item.replaceAll('summary', 'a') // summary
48
+ item = item.replaceAll('<a', '<a class="msg-xml-summary"')
49
+ item = item.replaceAll('<picture', '<img class="msg-xml-img"') // picture
50
+ // 将不正确的参数改为 dataset
51
+ item = item.replaceAll('size=', 'data-size=')
52
+ item = item.replaceAll('linespace=', 'data-linespace=')
53
+ item = item.replaceAll('cover=', 'src=')
54
+ // 处理出处标签
55
+ item = item.replace('source name=', 'source data-name=')
56
+ // 处理错误的 style 位置
57
+ const div = document.createElement('div')
58
+ div.id = 'xml-' + msgid
59
+ div.dataset.id = id
60
+ div.innerHTML = item
61
+ for (const element of div.children[0].children) {
62
+ if (element.nodeName === 'P') {
63
+ const pBody = element as HTMLParagraphElement
64
+ pBody.style.fontSize =
65
+ (Number(pBody.dataset.size) / 30).toString() + 'rem'
66
+ pBody.style.marginBottom = Number(pBody.dataset.size) / 5 + 'px'
67
+ break
68
+ }
69
+ }
70
+ // 解析 msg 消息体
71
+ let msgHeader =
72
+ xml.substring(xml.indexOf('<msg'), xml.indexOf('<item')) + '</msg>'
73
+ msgHeader = msgHeader.replace('msg', 'div')
74
+ msgHeader = msgHeader.replace('m_resid=', 'data-resid=')
75
+ msgHeader = msgHeader.replace('url=', 'data-url=')
76
+ const header = document.createElement('div')
77
+ header.innerHTML = msgHeader
78
+ // 处理特殊的出处
79
+ let sourceBody = undefined as HTMLElement | undefined
80
+ for (const element of div.children) {
81
+ if (element.nodeName === 'SOURCE') {
82
+ sourceBody = element as HTMLElement
83
+ }
84
+ }
85
+ if (sourceBody !== undefined) {
86
+ const source = sourceBody.dataset.name
87
+ if (source === '群投票')
88
+ return (
89
+ '<a class="msg-unknow">(' +
90
+ $t('chat_xml_unsupport') +
91
+ ':' +
92
+ source +
93
+ ')</a>'
94
+ )
95
+ }
96
+ // 附带链接的 xml 消息处理
97
+ if ((header.children[0] as HTMLElement).dataset.url !== undefined) {
98
+ link = (header.children[0] as HTMLElement).dataset.url
99
+ div.style.cursor = 'pointer'
100
+ }
101
+ return div.outerHTML
102
+ } catch (ex) {
103
+ new Logger().error(ex as Error, 'xml 消息解析错误')
104
+ return (
105
+ '<span v-else class="msg-unknown">( ' +
106
+ $t('解析消息错误') +
107
+ ': xml )</span>'
108
+ )
109
+ }
110
+ }
111
+
112
+ /**
113
+ * xml 消息的点击事件
114
+ */
115
+ function cardClick() {
116
+ const sender = mainDom.value
117
+
118
+ if (!sender) return
119
+
120
+ // 如果存在 url 项,优先打开 url
121
+ if (link !== undefined && link !== 'undefined' && link !== '') {
122
+ const openType =
123
+ sender.dataset.urlOpenType || sender.dataset.urlopentype
124
+ if (openType == '_self') {
125
+ window.open(link, '_self')
126
+ } else {
127
+ // 默认都以 _blank 打开
128
+ openLink(link)
129
+ }
130
+ }
131
+ }
132
+ </script>
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <div v-if="success" class="msg-json self-help">
3
+ <p>{{ parsedContent.title }}</p>
4
+ <span class="reply-buttons">
5
+ <span
6
+ v-for="(button, index) in parsedContent.button"
7
+ :key="index"
8
+ class="reply-button"
9
+ @click="sendNotify(button.action_data)">
10
+ {{ button.name }}
11
+ </span>
12
+ </span>
13
+ <div class="bottom-bar">
14
+ <font-awesome-icon icon="square-poll-horizontal" />
15
+ <span>{{ $t('自助问答') }}</span>
16
+ </div>
17
+ </div>
18
+ <span v-else class="msg-unknown">{{
19
+ '( ' + $t('加载失败') + ': ' + id + ' )'
20
+ }}</span>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
25
+ import { Logger } from '../../../function/base';
26
+ import { sendMsgRaw } from '../../../function/utils/msgUtil'
27
+ import { useChatStore } from '../../../state/chat'
28
+ import * as z from 'zod'
29
+
30
+ const { data: jsonData, id } = defineProps<{
31
+ data: string,
32
+ id: string,
33
+ }>()
34
+
35
+ const autoReply = z
36
+ .object({
37
+ app: z.literal('com.tencent.autoreply'),
38
+ meta: z.object({
39
+ metadata: z.object({
40
+ buttons: z.array(
41
+ z.object({
42
+ action: z.literal('notify'),
43
+ action_data: z.string(),
44
+ name: z.string(),
45
+ slot: z.number(),
46
+ }),
47
+ ),
48
+ title: z.string(),
49
+ type: z.string(),
50
+ }),
51
+ }),
52
+ })
53
+ .transform((o) => ({
54
+ title: o.meta.metadata.title,
55
+ button: o.meta.metadata.buttons.map((b) => ({
56
+ name: b.name,
57
+ action_data: b.action_data,
58
+ })),
59
+ }))
60
+ const json = JSON.parse(jsonData)
61
+ const parsedData = autoReply.safeParse(json)
62
+ const success = parsedData.success
63
+ const parsedContent = parsedData.data!
64
+ const chatStore = useChatStore()
65
+ if (!success) {
66
+ new Logger().error(parsedData.error, 'Card Parse Error')
67
+ }
68
+
69
+ function sendNotify(data: string) {
70
+ sendMsgRaw(
71
+ String(chatStore.chatInfo.show.id),
72
+ chatStore.chatInfo.show.type,
73
+ [
74
+ {
75
+ type: 'at',
76
+ qq: '2854196310'
77
+ },
78
+ {
79
+ type: 'text',
80
+ text: data,
81
+ }
82
+ ]
83
+ )
84
+ }
85
+ </script>
86
+
87
+ <style lang="css" scoped>
88
+ .reply-buttons {
89
+ display: flex;
90
+ flex-direction: column;
91
+ margin: 1ch 2ch;
92
+ }
93
+ .reply-button {
94
+ text-decoration: underline;
95
+ cursor: pointer;
96
+ }
97
+ </style>
@@ -0,0 +1,129 @@
1
+ <template>
2
+ <div v-if="success" class="msg-json">
3
+ <div class="contact-data">
4
+ <img :src="parsedContent.img" alt="">
5
+ <div>
6
+ <p>{{ parsedContent.title }}</p>
7
+ <span v-if="parsedContent.type === 'group'">{{ parsedContent.desc }}</span>
8
+ </div>
9
+ </div>
10
+ <div class="bottom-bar">
11
+ <span>{{ parsedContent.name }}</span>
12
+ </div>
13
+ </div>
14
+ <span v-else class="msg-unknown">{{
15
+ '( ' + $t('加载失败') + ': ' + id + ' )'
16
+ }}</span>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { Logger } from '../../../function/base';
21
+ import * as z from 'zod'
22
+
23
+ const { data: jsonData, id } = defineProps<{
24
+ data: string,
25
+ id: string,
26
+ }>()
27
+
28
+ const friend = z
29
+ .object({
30
+ app: z.literal('com.tencent.contact.lua'),
31
+ meta: z.object({
32
+ contact: z.object({
33
+ avatar: z.string(),
34
+ nickname: z.string(),
35
+ contact: z.string(),
36
+ jumpUrl: z.string(),
37
+ tag: z.literal('推荐好友'),
38
+ }),
39
+ }),
40
+ })
41
+ .transform((o) => ({
42
+ type: 'user' as const,
43
+ img: o.meta.contact.avatar,
44
+ title: o.meta.contact.nickname,
45
+ jumpUrl: o.meta.contact.jumpUrl,
46
+ name: o.meta.contact.tag,
47
+ }))
48
+
49
+ const group = z
50
+ .object({
51
+ app: z.literal('com.tencent.contact.lua'),
52
+ meta: z.object({
53
+ contact: z.object({
54
+ avatar: z.string().transform((s) => s.replace('/100', '/')),
55
+ nickname: z.string(),
56
+ contact: z.string().transform((s) => `${s}…`),
57
+ jumpUrl: z.string(),
58
+ tag: z.literal('群名片'),
59
+ }),
60
+ }),
61
+ })
62
+ .transform((o) => ({
63
+ type: 'group' as const,
64
+ img: o.meta.contact.avatar,
65
+ title: o.meta.contact.nickname,
66
+ desc: o.meta.contact.contact,
67
+ jumpUrl: o.meta.contact.jumpUrl,
68
+ name: o.meta.contact.tag,
69
+ }))
70
+
71
+ const bot = z
72
+ .object({
73
+ app: z.literal('com.tencent.contact.lua'),
74
+ meta: z.object({
75
+ contact: z.object({
76
+ avatar: z.string(),
77
+ nickname: z.string(),
78
+ contact: z.string(),
79
+ jumpUrl: z.string(),
80
+ tag: z.literal('机器人名片'),
81
+ }),
82
+ }),
83
+ })
84
+ .transform((o) => ({
85
+ type: 'bot' as const,
86
+ img: o.meta.contact.avatar,
87
+ title: o.meta.contact.nickname,
88
+ jumpUrl: o.meta.contact.jumpUrl,
89
+ name: o.meta.contact.tag,
90
+ }))
91
+
92
+ const contact = z.union([friend, group, bot])
93
+
94
+ const json = JSON.parse(jsonData)
95
+ const parsedData = contact.safeParse(json)
96
+ const success = parsedData.success
97
+ const parsedContent = parsedData.data!
98
+ if (!success) {
99
+ new Logger().error(parsedData.error, 'Card Parse Error')
100
+ }
101
+ </script>
102
+
103
+ <style scoped>
104
+ .contact-data {
105
+ display: flex;
106
+ align-items: center;
107
+ }
108
+ .contact-data img {
109
+ width: 45px;
110
+ height: 45px;
111
+ border-radius: 100%;
112
+ margin-right: 15px;
113
+ }
114
+ .contact-data > div {
115
+ display: flex;
116
+ flex-direction: column;
117
+ }
118
+ .contact-data p {
119
+ text-wrap: nowrap;
120
+ margin: 0;
121
+ font-weight: bold;
122
+ }
123
+ .contact-data span {
124
+ text-wrap: nowrap;
125
+ font-size: 0.8rem;
126
+ opacity: 0.7;
127
+ margin-top: 5px;
128
+ }
129
+ </style>
@@ -0,0 +1,94 @@
1
+ <template>
2
+ <div v-if="success" class="msg-json" @click="openLink(parsedContent.jumpUrl)">
3
+ <p>{{ parsedContent.title }}</p>
4
+ <span>{{ parsedContent.desc }}</span>
5
+ <img :src="parsedContent.img" alt="">
6
+ <div class="bottom-bar">
7
+ <img :src="parsedContent.icon" alt="">
8
+ <span>{{ parsedContent.name }}</span>
9
+ </div>
10
+ </div>
11
+ <span v-else class="msg-unknown">{{
12
+ '( ' + $t('加载失败') + ': ' + id + ' )'
13
+ }}</span>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { Logger } from '../../../function/base';
18
+ import { openLink } from '../../../function/utils/appUtil'
19
+ import * as z from 'zod'
20
+
21
+ const { data: jsonData, id } = defineProps<{
22
+ data: string,
23
+ id: string,
24
+ }>()
25
+
26
+ /**
27
+ * 解包 Qzone 风格的嵌套 URL 链接
28
+ * @param url 原始的完整 URL 字符串
29
+ * @returns 解码后的内部链接,如果解析失败则返回 null
30
+ */
31
+ function unpackNestedUrl(url: string): string | null {
32
+ const logger = new Logger()
33
+ try {
34
+ const urlObj = new URL(url)
35
+
36
+ // 1. 处理路径部分 (pathname)
37
+ // 示例路径: /groupphoto/inqq/detail/https%3A%2F%2F...
38
+ const pathname = urlObj.pathname
39
+
40
+ // 寻找 "detail/" 之后的部分,这是常见的 Qzone 嵌套模式
41
+ const detailMarker = 'detail/'
42
+ const markerIndex = pathname.indexOf(detailMarker)
43
+
44
+ if (markerIndex !== -1) {
45
+ // 截取 detail/ 之后的所有内容
46
+ const encodedPart = pathname.substring(
47
+ markerIndex + detailMarker.length,
48
+ )
49
+
50
+ // 进行 URL 解码
51
+ // 注意:decodeURIComponent 会处理 %3A, %2F, %26 等
52
+ return decodeURIComponent(encodedPart)
53
+ }
54
+
55
+ return null
56
+ } catch (error) {
57
+ logger.error(error as Error, 'URL 解包失败')
58
+ return null
59
+ }
60
+ }
61
+
62
+ const feed = z
63
+ .object({
64
+ app: z.literal('com.tencent.feed.lua'),
65
+ meta: z.object({
66
+ feed: z.object({
67
+ cover: z.string(),
68
+ title: z.string(),
69
+ tagIcon: z.string(),
70
+ tagName: z.string(),
71
+ forwardMessage: z.string(),
72
+ pcJumpUrl: z.string(),
73
+ }),
74
+ }),
75
+ prompt: z.string(),
76
+ })
77
+ .transform((o) => ({
78
+ title: o.meta.feed.title,
79
+ jumpUrl:
80
+ unpackNestedUrl(o.meta.feed.pcJumpUrl) ?? o.meta.feed.pcJumpUrl,
81
+ img: o.meta.feed.cover,
82
+ icon: o.meta.feed.tagIcon,
83
+ name: o.meta.feed.tagName,
84
+ desc: o.meta.feed.forwardMessage,
85
+ }))
86
+
87
+ const json = JSON.parse(jsonData)
88
+ const parsedData = feed.safeParse(json)
89
+ const success = parsedData.success
90
+ const parsedContent = parsedData.data!
91
+ if (!success) {
92
+ new Logger().error(parsedData.error, 'Card Parse Error')
93
+ }
94
+ </script>
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <div v-if="success" class="msg-json" @click="openLink(parsedContent.jumpUrl)">
3
+ <p>{{ parsedContent.title }}</p>
4
+ <span>
5
+ <font-awesome-icon icon="eye" /> {{ parsedContent.view }}
6
+ <font-awesome-icon icon="message" /> {{ parsedContent.comment }}
7
+ <font-awesome-icon icon="thumbs-up" /> {{ parsedContent.prefer }}
8
+ </span>
9
+ <img v-if="parsedContent.img" :src="parsedContent.img" alt=""
10
+ class="forum-img">
11
+ <div class="bottom-bar">
12
+ <img :src="parsedContent.icon" alt="">
13
+ <span>{{ parsedContent.name }} ({{ $t('频道') }})</span>
14
+ </div>
15
+ </div>
16
+ <span v-else class="msg-unknown">{{
17
+ '( ' + $t('加载失败') + ': ' + id + ' )'
18
+ }}</span>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { Logger } from '../../../function/base'
23
+ import { openLink } from '../../../function/utils/appUtil'
24
+ import * as z from 'zod'
25
+
26
+ const { data: jsonData, id } = defineProps<{
27
+ data: string,
28
+ id: string,
29
+ }>()
30
+
31
+ const forum = z
32
+ .object({
33
+ app: z.literal('com.tencent.forum'),
34
+ meta: z.object({
35
+ detail: z.object({
36
+ channel_info: z.object({
37
+ guild_icon: z.string(),
38
+ guild_name: z.string(),
39
+ }),
40
+ feed: z.object({
41
+ comment_count: z.number().optional(),
42
+ images: z
43
+ .array(
44
+ z.object({
45
+ pic_url: z.string(),
46
+ height: z.number(),
47
+ width: z.number(),
48
+ }),
49
+ )
50
+ .optional(),
51
+ prefer_count: z.number().optional(),
52
+ view_count: z.number().optional(),
53
+ }),
54
+ jump_url: z.string(),
55
+ }),
56
+ }),
57
+ prompt: z.string(),
58
+ })
59
+ .transform((o) => ({
60
+ title: o.prompt.replace('[频道帖子]', ''),
61
+ view: o.meta.detail.feed.view_count ?? 0,
62
+ comment: o.meta.detail.feed.comment_count ?? 0,
63
+ prefer: o.meta.detail.feed.prefer_count ?? 0,
64
+ jumpUrl: o.meta.detail.jump_url,
65
+ img: o.meta.detail.feed.images?.[0]?.pic_url,
66
+ icon: o.meta.detail.channel_info.guild_icon,
67
+ name: o.meta.detail.channel_info.guild_name,
68
+ }))
69
+ const json = JSON.parse(jsonData)
70
+ const parsedData = forum.safeParse(json)
71
+ const success = parsedData.success
72
+ const parsedContent = parsedData.data!
73
+ if (!success) {
74
+ new Logger().error(parsedData.error, 'Card Parse Error')
75
+ }
76
+ </script>
77
+
78
+ <style lang="css" scoped>
79
+ .forum-img {
80
+ max-width: 30vw;
81
+ min-width: 240px;
82
+ object-fit: cover;
83
+ }
84
+ </style>