cortico-world-forward-reader 0.1.0 → 0.1.2
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/describe.json +2 -2
- package/package.json +1 -1
- package/src/index.js +49 -29
- package/src/tools.js +3 -2
package/describe.json
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
"tags": [
|
|
6
6
|
"read"
|
|
7
7
|
],
|
|
8
|
-
"description": "拆开 QQ 的「合并转发 / 聊天记录」卡片,包括转发里套着的转发。\n什么时候用:收到 `qq.forward` 事件、或者 QQ 消息里写着「[合并转发]」但只摊开了一层、里面还有「又一层」时;想看清某段聊天记录到底是谁说了什么,也用它。\n怎么给编号:QQ 消息事件行首的 `#编号`(如 `#265277450`)就是 message_id,原样填进 ref;带不带
|
|
8
|
+
"description": "拆开 QQ 的「合并转发 / 聊天记录」卡片,包括转发里套着的转发。\n什么时候用:收到 `qq.forward` 事件、或者 QQ 消息里写着「[合并转发]」但只摊开了一层、里面还有「又一层」时;想看清某段聊天记录到底是谁说了什么,也用它。\n怎么给编号:QQ 消息事件行首的 `#编号`(如 `#265277450`)就是 message_id,原样填进 ref;带不带 `#`、前后有空格都不要紧。编号可能是负数(如 `#-1175570559`),负号是编号的一部分,要一起填,别去掉。\n它只读,只走本机的 SnowLuma OneBot HTTP 接口(默认 127.0.0.1:3000),不发消息、不改任何东西;接口没开或 token 不对会直接说清楚哪一步不对。输出过长会截断。",
|
|
9
9
|
"parameters": {
|
|
10
10
|
"type": "object",
|
|
11
11
|
"properties": {
|
|
12
12
|
"ref": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"description": "消息编号,即 QQ 消息事件行首的 #后面的数字,如 \"265277450\"。"
|
|
14
|
+
"description": "消息编号,即 QQ 消息事件行首的 #后面的数字,如 \"265277450\";负数就带负号一起填,如 \"-1175570559\"。"
|
|
15
15
|
},
|
|
16
16
|
"depth": {
|
|
17
17
|
"type": "integer",
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -40,13 +40,21 @@ function senderName(sender) {
|
|
|
40
40
|
return sender.card || sender.nickname || (sender.user_id ? String(sender.user_id) : '?');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** 今天的时间只给 HH:MM;跨天的带上日期,免得整份记录分不清哪天。 */
|
|
43
44
|
function clockOf(time) {
|
|
44
45
|
if (!time) return '';
|
|
45
46
|
const d = new Date(Number(time) * 1000);
|
|
46
47
|
if (Number.isNaN(d.getTime())) return '';
|
|
47
48
|
const hh = String(d.getHours()).padStart(2, '0');
|
|
48
49
|
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
49
|
-
|
|
50
|
+
const now = new Date();
|
|
51
|
+
const sameDay =
|
|
52
|
+
d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth() && d.getDate() === now.getDate();
|
|
53
|
+
if (sameDay) return `${hh}:${mm}`;
|
|
54
|
+
const mo = String(d.getMonth() + 1).padStart(2, '0');
|
|
55
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
56
|
+
const year = d.getFullYear() === now.getFullYear() ? '' : `${d.getFullYear()}-`;
|
|
57
|
+
return `${year}${mo}-${dd} ${hh}:${mm}`;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/** 把一条消息的段合成一行,并挑出里面嵌套的转发段。 */
|
|
@@ -126,46 +134,58 @@ export class ForwardReaderWorld {
|
|
|
126
134
|
const cfg = this.cfg;
|
|
127
135
|
const rawRef = String(args.ref ?? '')
|
|
128
136
|
.trim()
|
|
129
|
-
.replace(/^#/, '')
|
|
137
|
+
.replace(/^#/, '')
|
|
138
|
+
.trim();
|
|
130
139
|
if (!rawRef) return fail('要给消息编号(QQ 消息行首的 #后面那串数字)。');
|
|
131
|
-
|
|
140
|
+
// QQ 的 message_id 有负数的(合并转发卡片就常见):负号是 id 的一部分,不能丢。
|
|
141
|
+
if (!/^-?\d+$/.test(rawRef)) return fail(`"${rawRef}" 不像消息编号,应该是数字(前面可能带一个负号),比如 265277450 或 -1175570559。`);
|
|
132
142
|
|
|
133
143
|
const maxDepth = clampInt(args.depth ?? cfg.maxDepth, 8, 1, 20);
|
|
134
144
|
const limit = clampInt(args.maxChars ?? cfg.maxChars, MAX_OUTPUT_CHARS, 1000, 60000);
|
|
135
145
|
|
|
146
|
+
let data;
|
|
147
|
+
try {
|
|
148
|
+
data = await callOneBot(cfg, 'get_msg', { message_id: rawRef });
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const why = String(err?.message || err);
|
|
151
|
+
const hint = /not found|不存在|找不到/i.test(why)
|
|
152
|
+
? '取不到这条,常见原因:消息太老、SnowLuma 那边没缓存了;或者编号抄错——负号是编号的一部分,除了行首的「#」别的字符一个都不能少。'
|
|
153
|
+
: '';
|
|
154
|
+
return fail(`取消息 ${rawRef} 失败:${why}${hint ? '\n' + hint : ''}\n可以先跑 qq_forward_status 看看接口和 token 怎么样。`);
|
|
155
|
+
}
|
|
156
|
+
|
|
136
157
|
const out = [];
|
|
137
158
|
const state = { items: 0, levels: 0, truncated: false };
|
|
159
|
+
const segments = data?.message || [];
|
|
160
|
+
const whole = foldMessage(segments);
|
|
161
|
+
const who = senderName(data?.sender);
|
|
162
|
+
const when = clockOf(data?.time);
|
|
163
|
+
|
|
164
|
+
if (!whole.nested.length) {
|
|
165
|
+
// 不是合并转发:直接把这条消息本身给人看。
|
|
166
|
+
out.push(`# 消息 ${rawRef}${who !== '?' ? ` · ${who}` : ''}${when ? ` · ${when}` : ''}`);
|
|
167
|
+
out.push('');
|
|
168
|
+
out.push(whole.text || '(没有文字内容)');
|
|
169
|
+
out.push('');
|
|
170
|
+
out.push('这条不是合并转发卡片,所以没有可拆的层。');
|
|
171
|
+
return cap(out.join('\n'), limit);
|
|
172
|
+
}
|
|
138
173
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const segments = data?.message || [];
|
|
142
|
-
const whole = foldMessage(segments);
|
|
143
|
-
|
|
144
|
-
if (!whole.nested.length) {
|
|
145
|
-
// 不是合并转发:直接把这条消息本身给人看。
|
|
146
|
-
const who = senderName(data?.sender);
|
|
147
|
-
const when = clockOf(data?.time);
|
|
148
|
-
out.push(`# 消息 ${rawRef}${who !== '?' ? ` · ${who}` : ''}${when ? ` · ${when}` : ''}`);
|
|
149
|
-
out.push('');
|
|
150
|
-
out.push(whole.text || '(没有文字内容)');
|
|
151
|
-
out.push('');
|
|
152
|
-
out.push('这条不是合并转发卡片,所以没有可拆的层。');
|
|
153
|
-
return cap(out.join('\n'), limit);
|
|
154
|
-
}
|
|
174
|
+
out.push(`# 转发拆解 · 消息 ${rawRef}${who !== '?' ? ` · ${who}` : ''}${when ? ` · ${when}` : ''}`);
|
|
175
|
+
out.push('');
|
|
155
176
|
|
|
156
|
-
|
|
157
|
-
|
|
177
|
+
// 卡片本身带的文字也要留下,不然只列转发内容就把原话吞掉了。
|
|
178
|
+
if (whole.text) {
|
|
179
|
+
out.push(`(这张卡片上还写着)${whole.text}`);
|
|
158
180
|
out.push('');
|
|
181
|
+
}
|
|
159
182
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
out.unshift(`共 ${state.levels} 层 / ${state.items} 条${state.truncated ? '(已达条数上限,后面没再往下翻)' : ''}`);
|
|
165
|
-
return cap(out.join('\n'), limit);
|
|
166
|
-
} catch (err) {
|
|
167
|
-
return fail(`拆不开:${err.message}\n可以先跑 qq_forward_status 看看接口和 token 怎么样。`);
|
|
183
|
+
for (const seg of whole.nested) {
|
|
184
|
+
await this.walk(seg.data?.id, 0, out, state, maxDepth);
|
|
168
185
|
}
|
|
186
|
+
|
|
187
|
+
out.unshift(`共 ${state.levels} 层 / ${state.items} 条${state.truncated ? '(已达条数上限,后面没再往下翻)' : ''}`);
|
|
188
|
+
return cap(out.join('\n'), limit);
|
|
169
189
|
}
|
|
170
190
|
|
|
171
191
|
async walk(forwardId, depth, out, state, maxDepth) {
|
package/src/tools.js
CHANGED
|
@@ -15,7 +15,8 @@ export const TOOL_DECLS = [
|
|
|
15
15
|
'拆开 QQ 的「合并转发 / 聊天记录」卡片,包括转发里套着的转发。\n' +
|
|
16
16
|
'什么时候用:收到 `qq.forward` 事件、或者 QQ 消息里写着「[合并转发]」但只摊开了一层、里面还有「又一层」时;' +
|
|
17
17
|
'想看清某段聊天记录到底是谁说了什么,也用它。\n' +
|
|
18
|
-
'怎么给编号:QQ 消息事件行首的 `#编号`(如 `#265277450`)就是 message_id,原样填进 ref;带不带
|
|
18
|
+
'怎么给编号:QQ 消息事件行首的 `#编号`(如 `#265277450`)就是 message_id,原样填进 ref;带不带 `#`、前后有空格都不要紧。' +
|
|
19
|
+
'编号可能是负数(如 `#-1175570559`),负号是编号的一部分,要一起填,别去掉。\n' +
|
|
19
20
|
'它只读,只走本机的 SnowLuma OneBot HTTP 接口(默认 127.0.0.1:3000),不发消息、不改任何东西;' +
|
|
20
21
|
'接口没开或 token 不对会直接说清楚哪一步不对。输出过长会截断。',
|
|
21
22
|
parameters: {
|
|
@@ -23,7 +24,7 @@ export const TOOL_DECLS = [
|
|
|
23
24
|
properties: {
|
|
24
25
|
ref: {
|
|
25
26
|
type: 'string',
|
|
26
|
-
description: '消息编号,即 QQ 消息事件行首的 #后面的数字,如 "265277450"。',
|
|
27
|
+
description: '消息编号,即 QQ 消息事件行首的 #后面的数字,如 "265277450";负数就带负号一起填,如 "-1175570559"。',
|
|
27
28
|
},
|
|
28
29
|
depth: {
|
|
29
30
|
type: 'integer',
|