@will-17173/telegram-cli 0.5.0 → 0.6.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/README.md +13 -0
- package/README.zh-CN.md +13 -0
- package/dist/cli/app.js +1 -1
- package/dist/commands/telegram.js +285 -15
- package/dist/commands/telegram.js.map +1 -1
- package/dist/services/download-service.js +321 -0
- package/dist/services/download-service.js.map +1 -0
- package/dist/services/sync-service.js +14 -3
- package/dist/services/sync-service.js.map +1 -1
- package/dist/storage/message-db.js +48 -12
- package/dist/storage/message-db.js.map +1 -1
- package/dist/telegram/mtcute-client.js +3 -3
- package/dist/telegram/mtcute-client.js.map +1 -1
- package/dist/telegram/raw-media-location.js +132 -0
- package/dist/telegram/raw-media-location.js.map +1 -0
- package/dist/telegram/types.js.map +1 -1
- package/dist/web/api.js +111 -2
- package/dist/web/api.js.map +1 -1
- package/dist/web/assets/{index-DC74wrJK.js → index-BNk4_1nk.js} +1 -1
- package/dist/web/assets/index-Dn2XqKTr.css +1 -0
- package/dist/web/index.html +2 -2
- package/dist/web/query.js +133 -11
- package/dist/web/query.js.map +1 -1
- package/dist/web/sync-task.js +5 -1
- package/dist/web/sync-task.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-B5v_8ify.css +0 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, open, rename, rm } from 'node:fs/promises';
|
|
3
|
+
import { dirname, extname, join, parse } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { sanitizeAttachmentFileName } from './attachment-download.js';
|
|
6
|
+
const MAX_FLOOD_RETRIES = 5;
|
|
7
|
+
export class DownloadService {
|
|
8
|
+
source;
|
|
9
|
+
sleep;
|
|
10
|
+
exists;
|
|
11
|
+
uuid;
|
|
12
|
+
constructor(source, dependencies = {}) {
|
|
13
|
+
this.source = source;
|
|
14
|
+
this.sleep = dependencies.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
15
|
+
this.exists = dependencies.exists ?? existsSync;
|
|
16
|
+
this.uuid = dependencies.uuid ?? randomUUID;
|
|
17
|
+
}
|
|
18
|
+
async download(input) {
|
|
19
|
+
const invalid = validateDownloadInput(input);
|
|
20
|
+
if (invalid != null)
|
|
21
|
+
return invalid;
|
|
22
|
+
if (input.groupedId != null && input.groupedMessages == null) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
error: {
|
|
26
|
+
code: 'download_grouped_id_not_resolved',
|
|
27
|
+
message: `Grouped album ${input.groupedId} was not resolved from the local message cache.`,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const targets = await this.collectTargets(input);
|
|
32
|
+
if ((input.messageId != null || input.groupedId != null) && input.attachment != null && targets.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
error: {
|
|
36
|
+
code: 'download_attachment_not_found',
|
|
37
|
+
message: `${input.groupedId == null ? `Message ${input.messageId}` : `Grouped album ${input.groupedId}`} does not have attachment ${input.attachment}.`,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const result = {
|
|
42
|
+
chat: input.chat,
|
|
43
|
+
output: input.output,
|
|
44
|
+
requested: targets.length,
|
|
45
|
+
downloaded: 0,
|
|
46
|
+
skipped: this.lastSkipped,
|
|
47
|
+
failed: 0,
|
|
48
|
+
flood_waits: 0,
|
|
49
|
+
files: [],
|
|
50
|
+
failures: [],
|
|
51
|
+
};
|
|
52
|
+
await mkdir(input.output, { recursive: true });
|
|
53
|
+
await this.runDownloads(targets, Math.max(1, Math.floor(input.concurrency ?? 3)), result);
|
|
54
|
+
result.files.sort((left, right) => right.msg_id - left.msg_id || left.attachment - right.attachment);
|
|
55
|
+
const output = { ...result, human: downloadSummary(result) };
|
|
56
|
+
if (result.failed > 0) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
error: {
|
|
60
|
+
code: 'download_partial_failure',
|
|
61
|
+
message: 'Download completed with one or more attachment failures.',
|
|
62
|
+
details: output,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { ok: true, data: result, human: output.human };
|
|
67
|
+
}
|
|
68
|
+
lastSkipped = 0;
|
|
69
|
+
async collectTargets(input) {
|
|
70
|
+
this.lastSkipped = 0;
|
|
71
|
+
if (input.groupedMessages != null)
|
|
72
|
+
return this.targetsFromGroupedMessages(input.groupedMessages, input);
|
|
73
|
+
const targets = [];
|
|
74
|
+
const reserved = new Set();
|
|
75
|
+
const lower = lowerMessageId(input);
|
|
76
|
+
const upper = upperMessageId(input);
|
|
77
|
+
const minId = lower == null ? undefined : lower - 1;
|
|
78
|
+
for await (const page of this.source.iterHistoryPages({
|
|
79
|
+
chat: input.chat,
|
|
80
|
+
...(input.since == null ? {} : { since: input.since }),
|
|
81
|
+
...(input.until == null ? {} : { until: input.until }),
|
|
82
|
+
...(minId == null ? {} : { minId }),
|
|
83
|
+
})) {
|
|
84
|
+
for (const message of page) {
|
|
85
|
+
if (lower != null && message.msg_id < lower)
|
|
86
|
+
continue;
|
|
87
|
+
if (upper != null && message.msg_id > upper)
|
|
88
|
+
continue;
|
|
89
|
+
if (!matchesDateRange(message, input))
|
|
90
|
+
continue;
|
|
91
|
+
if (message.attachment?.downloadable !== true) {
|
|
92
|
+
if (message.attachment != null)
|
|
93
|
+
this.lastSkipped += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const attachmentNumber = 1;
|
|
97
|
+
if (input.attachment != null && input.attachment !== attachmentNumber)
|
|
98
|
+
continue;
|
|
99
|
+
const destination = uniqueDestination(input.output, fileNameForMessage(message), this.exists, reserved);
|
|
100
|
+
reserved.add(destination);
|
|
101
|
+
targets.push({ chat: input.chat, message, attachment: attachmentNumber, destination });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return targets;
|
|
105
|
+
}
|
|
106
|
+
targetsFromGroupedMessages(messages, input) {
|
|
107
|
+
const reserved = new Set();
|
|
108
|
+
const ordered = [...messages].sort((left, right) => left.msg_id - right.msg_id);
|
|
109
|
+
const targets = [];
|
|
110
|
+
ordered.forEach((message, index) => {
|
|
111
|
+
const attachmentNumber = index + 1;
|
|
112
|
+
if (input.attachment != null && input.attachment !== attachmentNumber)
|
|
113
|
+
return;
|
|
114
|
+
if (message.attachment?.downloadable !== true) {
|
|
115
|
+
if (message.attachment != null)
|
|
116
|
+
this.lastSkipped += 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const destination = uniqueDestination(input.output, fileNameForMessage(message), this.exists, reserved);
|
|
120
|
+
reserved.add(destination);
|
|
121
|
+
targets.push({ chat: input.chat, message, attachment: attachmentNumber, destination });
|
|
122
|
+
});
|
|
123
|
+
return targets;
|
|
124
|
+
}
|
|
125
|
+
async runDownloads(targets, concurrency, result) {
|
|
126
|
+
let index = 0;
|
|
127
|
+
const worker = async () => {
|
|
128
|
+
while (index < targets.length) {
|
|
129
|
+
const target = targets[index];
|
|
130
|
+
index += 1;
|
|
131
|
+
await this.downloadOne(target, result);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, targets.length) }, worker));
|
|
135
|
+
}
|
|
136
|
+
async downloadOne(target, result) {
|
|
137
|
+
await mkdir(dirname(target.destination), { recursive: true });
|
|
138
|
+
const temporary = join(dirname(target.destination), `.telegram-cli-download-${this.uuid()}.part`);
|
|
139
|
+
const handle = await open(temporary, 'wx');
|
|
140
|
+
await handle.close();
|
|
141
|
+
try {
|
|
142
|
+
let floodRetries = 0;
|
|
143
|
+
while (true) {
|
|
144
|
+
try {
|
|
145
|
+
await this.source.downloadMedia({
|
|
146
|
+
chat: target.chat,
|
|
147
|
+
messageId: target.message.msg_id,
|
|
148
|
+
destination: temporary,
|
|
149
|
+
});
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const seconds = floodWaitSeconds(error);
|
|
154
|
+
if (!Number.isFinite(seconds) || floodRetries >= MAX_FLOOD_RETRIES)
|
|
155
|
+
throw error;
|
|
156
|
+
floodRetries += 1;
|
|
157
|
+
result.flood_waits += 1;
|
|
158
|
+
await this.sleep((seconds + 1) * 1000);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
await rename(temporary, target.destination);
|
|
162
|
+
result.downloaded += 1;
|
|
163
|
+
result.files.push({
|
|
164
|
+
chat_id: target.message.chat_id,
|
|
165
|
+
msg_id: target.message.msg_id,
|
|
166
|
+
attachment: target.attachment,
|
|
167
|
+
path: target.destination,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
result.failed += 1;
|
|
172
|
+
result.failures.push({
|
|
173
|
+
msg_id: target.message.msg_id,
|
|
174
|
+
attachment: target.attachment,
|
|
175
|
+
error: errorMessage(error),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
await rm(temporary, { force: true }).catch(() => undefined);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function validateDownloadInput(input) {
|
|
184
|
+
if (String(input.chat).trim() === '')
|
|
185
|
+
return invalidOption('chat must be a non-empty string.');
|
|
186
|
+
if (!input.output.trim())
|
|
187
|
+
return invalidOption('output must be a non-empty path.');
|
|
188
|
+
if (input.messageId != null && !isPositiveInteger(input.messageId))
|
|
189
|
+
return invalidOption('message id must be a positive integer.');
|
|
190
|
+
if (input.attachment != null && !isPositiveInteger(input.attachment))
|
|
191
|
+
return invalidOption('attachment must be a positive integer.');
|
|
192
|
+
if (input.fromId != null && !isPositiveInteger(input.fromId))
|
|
193
|
+
return invalidOption('from id must be a positive integer.');
|
|
194
|
+
if (input.toId != null && !isPositiveInteger(input.toId))
|
|
195
|
+
return invalidOption('to id must be a positive integer.');
|
|
196
|
+
if (input.concurrency != null && !isPositiveInteger(input.concurrency))
|
|
197
|
+
return invalidOption('concurrency must be a positive integer.');
|
|
198
|
+
if (input.since != null && input.until != null && input.since.getTime() >= input.until.getTime()) {
|
|
199
|
+
return invalidOption('since must be earlier than until.');
|
|
200
|
+
}
|
|
201
|
+
const scopes = [
|
|
202
|
+
input.messageId != null,
|
|
203
|
+
input.groupedId != null,
|
|
204
|
+
input.fromId != null || input.toId != null,
|
|
205
|
+
input.since != null || input.until != null,
|
|
206
|
+
input.all === true,
|
|
207
|
+
].filter(Boolean).length;
|
|
208
|
+
if (scopes !== 1) {
|
|
209
|
+
return invalidOption('Select exactly one download scope: message id, --grouped-id, --from/--to, --date/--since/--until, or --all.');
|
|
210
|
+
}
|
|
211
|
+
if (input.groupedId != null && input.groupedId.trim() === '')
|
|
212
|
+
return invalidOption('grouped id must be a non-empty string.');
|
|
213
|
+
if (input.attachment != null && input.messageId == null && input.groupedId == null) {
|
|
214
|
+
return invalidOption('--attachment can only be used with a single message id or --grouped-id.');
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
function invalidOption(message) {
|
|
219
|
+
return { ok: false, error: { code: 'invalid_option', message } };
|
|
220
|
+
}
|
|
221
|
+
function isPositiveInteger(value) {
|
|
222
|
+
return Number.isInteger(value) && value > 0;
|
|
223
|
+
}
|
|
224
|
+
function lowerMessageId(input) {
|
|
225
|
+
if (input.messageId != null)
|
|
226
|
+
return input.messageId;
|
|
227
|
+
if (input.fromId != null && input.toId != null)
|
|
228
|
+
return Math.min(input.fromId, input.toId);
|
|
229
|
+
return input.fromId ?? input.toId;
|
|
230
|
+
}
|
|
231
|
+
function upperMessageId(input) {
|
|
232
|
+
if (input.messageId != null)
|
|
233
|
+
return input.messageId;
|
|
234
|
+
if (input.fromId != null && input.toId != null)
|
|
235
|
+
return Math.max(input.fromId, input.toId);
|
|
236
|
+
return input.fromId ?? input.toId;
|
|
237
|
+
}
|
|
238
|
+
function matchesDateRange(message, input) {
|
|
239
|
+
const timestamp = Date.parse(message.timestamp);
|
|
240
|
+
if (!Number.isFinite(timestamp))
|
|
241
|
+
return false;
|
|
242
|
+
if (input.since != null && timestamp < input.since.getTime())
|
|
243
|
+
return false;
|
|
244
|
+
if (input.until != null && timestamp >= input.until.getTime())
|
|
245
|
+
return false;
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
function fileNameForMessage(message) {
|
|
249
|
+
const raw = message.attachment?.file_name?.trim();
|
|
250
|
+
if (raw)
|
|
251
|
+
return raw;
|
|
252
|
+
const extension = extensionForAttachment(message.attachment?.type);
|
|
253
|
+
return `${message.chat_id}-${message.msg_id}.${extension}`;
|
|
254
|
+
}
|
|
255
|
+
function extensionForAttachment(type) {
|
|
256
|
+
switch (type?.toLowerCase()) {
|
|
257
|
+
case 'photo':
|
|
258
|
+
return 'jpg';
|
|
259
|
+
case 'video':
|
|
260
|
+
return 'mp4';
|
|
261
|
+
case 'audio':
|
|
262
|
+
return 'mp3';
|
|
263
|
+
case 'voice':
|
|
264
|
+
return 'ogg';
|
|
265
|
+
case 'sticker':
|
|
266
|
+
return 'webp';
|
|
267
|
+
case 'animation':
|
|
268
|
+
return 'mp4';
|
|
269
|
+
default:
|
|
270
|
+
return 'bin';
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function uniqueDestination(directory, fileName, exists, reserved) {
|
|
274
|
+
const safeName = sanitizeAttachmentFileName(fileName);
|
|
275
|
+
let destination = join(directory, safeName);
|
|
276
|
+
if (!exists(destination) && !reserved.has(destination))
|
|
277
|
+
return destination;
|
|
278
|
+
const extension = extname(safeName);
|
|
279
|
+
const baseName = parse(safeName).name;
|
|
280
|
+
let index = 2;
|
|
281
|
+
do {
|
|
282
|
+
destination = join(directory, `${baseName} (${index})${extension}`);
|
|
283
|
+
index += 1;
|
|
284
|
+
} while (exists(destination) || reserved.has(destination));
|
|
285
|
+
return destination;
|
|
286
|
+
}
|
|
287
|
+
function floodWaitSeconds(error) {
|
|
288
|
+
if (error != null && typeof error === 'object') {
|
|
289
|
+
const maybe = error;
|
|
290
|
+
if (typeof maybe.seconds === 'number' && Number.isFinite(maybe.seconds))
|
|
291
|
+
return maybe.seconds;
|
|
292
|
+
const text = typeof maybe.text === 'string' ? maybe.text : typeof maybe.message === 'string' ? maybe.message : '';
|
|
293
|
+
const match = /FLOOD_WAIT_(\d+)/.exec(text);
|
|
294
|
+
if (match != null)
|
|
295
|
+
return Number(match[1]);
|
|
296
|
+
}
|
|
297
|
+
return Number.NaN;
|
|
298
|
+
}
|
|
299
|
+
function errorMessage(error) {
|
|
300
|
+
return error instanceof Error ? error.message : String(error);
|
|
301
|
+
}
|
|
302
|
+
function downloadSummary(result) {
|
|
303
|
+
return {
|
|
304
|
+
kind: 'summary',
|
|
305
|
+
title: 'Download',
|
|
306
|
+
fields: [
|
|
307
|
+
{ label: 'Requested', value: String(result.requested) },
|
|
308
|
+
{ label: 'Downloaded', value: String(result.downloaded), tone: result.failed === 0 ? 'success' : 'warning' },
|
|
309
|
+
{ label: 'Skipped', value: String(result.skipped) },
|
|
310
|
+
{ label: 'Failed', value: String(result.failed), tone: result.failed === 0 ? 'default' : 'danger' },
|
|
311
|
+
{ label: 'Flood Waits', value: String(result.flood_waits), tone: result.flood_waits === 0 ? 'default' : 'warning' },
|
|
312
|
+
{ label: 'Output', value: result.output },
|
|
313
|
+
],
|
|
314
|
+
table: {
|
|
315
|
+
columns: ['MESSAGE', 'ATTACHMENT', 'PATH'],
|
|
316
|
+
rows: result.files.map((file) => [String(file.msg_id), String(file.attachment), file.path]),
|
|
317
|
+
emptyText: 'No media downloaded.',
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=download-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download-service.js","sourceRoot":"","sources":["../../src/services/download-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,10 +13,13 @@ export class SyncService {
|
|
|
13
13
|
const invalid = validateHistoryOptions(options);
|
|
14
14
|
if (invalid)
|
|
15
15
|
return invalid;
|
|
16
|
+
const chatId = this.db.resolveChatId(options.chat);
|
|
17
|
+
const offset = chatId == null ? null : this.db.getFirstMsgOffset(chatId);
|
|
16
18
|
try {
|
|
17
19
|
const messages = await this.tg.fetchHistory({
|
|
18
20
|
chat: parseChat(options.chat),
|
|
19
21
|
limit: options.limit,
|
|
22
|
+
...(offset == null ? {} : { offset }),
|
|
20
23
|
pageDelay: options.pageDelay,
|
|
21
24
|
onProgress: options.onProgress,
|
|
22
25
|
});
|
|
@@ -86,18 +89,26 @@ export class SyncService {
|
|
|
86
89
|
const results = {};
|
|
87
90
|
const failures = {};
|
|
88
91
|
for (let index = 0; index < selected.length; index += 1) {
|
|
92
|
+
if (options.stopSignal?.aborted)
|
|
93
|
+
break;
|
|
89
94
|
const dialog = selected[index];
|
|
90
95
|
const lastId = this.db.getLastMsgId(dialog.id) ?? 0;
|
|
91
96
|
const limit = lastId === 0 && options.limit > FIRST_SYNC_LIMIT ? FIRST_SYNC_LIMIT : options.limit;
|
|
97
|
+
const onProgress = options.onProgress == null
|
|
98
|
+
? undefined
|
|
99
|
+
: (count) => options.onProgress?.(dialog.name, count);
|
|
100
|
+
options.onChatStart?.(dialog.name);
|
|
92
101
|
try {
|
|
93
|
-
const messages = await this.tg.fetchHistory({ chat: dialog.id, limit, minId: lastId, pageDelay: 1 });
|
|
102
|
+
const messages = await this.tg.fetchHistory({ chat: dialog.id, limit, minId: lastId, pageDelay: 1, onProgress });
|
|
94
103
|
results[dialog.name] = this.db.insertBatch(messages);
|
|
104
|
+
options.onChatComplete?.(dialog.name, results[dialog.name]);
|
|
95
105
|
}
|
|
96
106
|
catch (error) {
|
|
97
107
|
results[dialog.name] = 0;
|
|
98
108
|
failures[dialog.name] = errorMessage(error);
|
|
109
|
+
options.onChatComplete?.(dialog.name, 0, failures[dialog.name]);
|
|
99
110
|
}
|
|
100
|
-
if (options.delay > 0 && index < selected.length - 1) {
|
|
111
|
+
if (!options.stopSignal?.aborted && options.delay > 0 && index < selected.length - 1) {
|
|
101
112
|
const jitter = options.delay * (Math.random() * 0.4 - 0.2);
|
|
102
113
|
await delayMs((options.delay + jitter) * 1000);
|
|
103
114
|
}
|
|
@@ -108,7 +119,7 @@ export class SyncService {
|
|
|
108
119
|
.map(([name]) => name);
|
|
109
120
|
const data = {
|
|
110
121
|
new_messages: Object.values(results).reduce((sum, count) => sum + count, 0),
|
|
111
|
-
chats:
|
|
122
|
+
chats: Object.keys(results).length,
|
|
112
123
|
updated_chats,
|
|
113
124
|
results,
|
|
114
125
|
failures,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-service.js","sourceRoot":"","sources":["../../src/services/sync-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAE5D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAGpD,MAAM,gBAAgB,GAAG,GAAG,CAAA;
|
|
1
|
+
{"version":3,"file":"sync-service.js","sourceRoot":"","sources":["../../src/services/sync-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os';
|
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
6
|
import { getDbPath } from '../config/env.js';
|
|
7
7
|
import { canonicalChatId } from './chat-resolver.js';
|
|
8
|
+
import { extractGroupedId } from '../telegram/raw-message.js';
|
|
8
9
|
let snapshotCopyHook;
|
|
9
10
|
export function __setMessageDbSnapshotCopyHookForTests(hook) {
|
|
10
11
|
const previous = snapshotCopyHook;
|
|
@@ -64,6 +65,7 @@ export class MessageDB {
|
|
|
64
65
|
content TEXT,
|
|
65
66
|
timestamp TEXT NOT NULL,
|
|
66
67
|
raw_json TEXT,
|
|
68
|
+
preview_jpeg_base64 TEXT,
|
|
67
69
|
UNIQUE(platform, chat_id, msg_id)
|
|
68
70
|
);
|
|
69
71
|
CREATE INDEX IF NOT EXISTS idx_messages_chat_ts ON messages(chat_id, timestamp);
|
|
@@ -72,6 +74,7 @@ export class MessageDB {
|
|
|
72
74
|
CREATE INDEX IF NOT EXISTS idx_messages_content ON messages(content);
|
|
73
75
|
CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_name);
|
|
74
76
|
`);
|
|
77
|
+
this.ensurePreviewColumn();
|
|
75
78
|
}
|
|
76
79
|
static async openReadonly(path) {
|
|
77
80
|
const { snapshotDir, snapshotPath } = await createConsistentSnapshotAsync(path);
|
|
@@ -88,8 +91,8 @@ export class MessageDB {
|
|
|
88
91
|
return 0;
|
|
89
92
|
const stmt = this.db.prepare(`
|
|
90
93
|
INSERT OR IGNORE INTO messages
|
|
91
|
-
(platform, chat_id, chat_name, msg_id, sender_id, sender_name, content, timestamp, raw_json)
|
|
92
|
-
VALUES (@platform, @chat_id, @chat_name, @msg_id, @sender_id, @sender_name, @content, @timestamp, @raw_json)
|
|
94
|
+
(platform, chat_id, chat_name, msg_id, sender_id, sender_name, content, timestamp, raw_json, preview_jpeg_base64)
|
|
95
|
+
VALUES (@platform, @chat_id, @chat_name, @msg_id, @sender_id, @sender_name, @content, @timestamp, @raw_json, @preview_jpeg_base64)
|
|
93
96
|
`);
|
|
94
97
|
let inserted = 0;
|
|
95
98
|
const tx = this.db.transaction((rows) => {
|
|
@@ -103,8 +106,8 @@ export class MessageDB {
|
|
|
103
106
|
insertMessage(message) {
|
|
104
107
|
const stmt = this.db.prepare(`
|
|
105
108
|
INSERT OR IGNORE INTO messages
|
|
106
|
-
(platform, chat_id, chat_name, msg_id, sender_id, sender_name, content, timestamp, raw_json)
|
|
107
|
-
VALUES (@platform, @chat_id, @chat_name, @msg_id, @sender_id, @sender_name, @content, @timestamp, @raw_json)
|
|
109
|
+
(platform, chat_id, chat_name, msg_id, sender_id, sender_name, content, timestamp, raw_json, preview_jpeg_base64)
|
|
110
|
+
VALUES (@platform, @chat_id, @chat_name, @msg_id, @sender_id, @sender_name, @content, @timestamp, @raw_json, @preview_jpeg_base64)
|
|
108
111
|
`);
|
|
109
112
|
return this.insertPrepared(stmt, message) === 1;
|
|
110
113
|
}
|
|
@@ -156,10 +159,24 @@ export class MessageDB {
|
|
|
156
159
|
getMessagesPage(options) {
|
|
157
160
|
const params = [canonicalChatId(options.chatId)];
|
|
158
161
|
const conditions = ['chat_id = ?'];
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
162
|
+
const q = options.q?.trim();
|
|
163
|
+
if (q) {
|
|
164
|
+
conditions.push('(content LIKE ? OR sender_name LIKE ? OR CAST(sender_id AS TEXT) LIKE ?)');
|
|
165
|
+
params.push(`%${q}%`, `%${q}%`, `%${q}%`);
|
|
166
|
+
}
|
|
167
|
+
if (options.senderId != null) {
|
|
168
|
+
conditions.push('sender_id = ?');
|
|
169
|
+
params.push(options.senderId);
|
|
170
|
+
}
|
|
171
|
+
const senderName = options.senderName?.trim();
|
|
172
|
+
if (senderName) {
|
|
173
|
+
conditions.push('sender_name LIKE ?');
|
|
174
|
+
params.push(`%${senderName}%`);
|
|
175
|
+
}
|
|
176
|
+
const text = options.text?.trim();
|
|
177
|
+
if (text) {
|
|
161
178
|
conditions.push('content LIKE ?');
|
|
162
|
-
params.push(`%${
|
|
179
|
+
params.push(`%${text}%`);
|
|
163
180
|
}
|
|
164
181
|
if (options.since) {
|
|
165
182
|
conditions.push('timestamp >= ?');
|
|
@@ -174,18 +191,24 @@ export class MessageDB {
|
|
|
174
191
|
conditions.push('(timestamp, id) < (?, ?)');
|
|
175
192
|
params.push(cursor.timestamp, cursor.id);
|
|
176
193
|
}
|
|
194
|
+
const total = this.db.prepare(`
|
|
195
|
+
SELECT COUNT(*) AS count FROM messages INDEXED BY idx_messages_chat_recent
|
|
196
|
+
WHERE ${conditions.join(' AND ')}
|
|
197
|
+
`).get(...params).count;
|
|
177
198
|
const limit = clampInteger(options.limit, 50, 1, 100);
|
|
199
|
+
const offset = Math.max(0, Math.trunc(options.offset ?? 0));
|
|
178
200
|
const rows = this.db.prepare(`
|
|
179
201
|
SELECT * FROM messages INDEXED BY idx_messages_chat_recent
|
|
180
202
|
WHERE ${conditions.join(' AND ')}
|
|
181
203
|
ORDER BY timestamp DESC, id DESC
|
|
182
204
|
LIMIT ?
|
|
183
|
-
|
|
205
|
+
OFFSET ?
|
|
206
|
+
`).all(...params, limit + 1, offset);
|
|
184
207
|
const items = rows.slice(0, limit);
|
|
185
208
|
const next_cursor = rows.length > limit && items.length > 0
|
|
186
209
|
? encodeMessageCursor(items[items.length - 1])
|
|
187
210
|
: null;
|
|
188
|
-
return { items, next_cursor };
|
|
211
|
+
return { items, total, next_cursor };
|
|
189
212
|
}
|
|
190
213
|
getMessagesByKeys(keys) {
|
|
191
214
|
if (keys.length === 0)
|
|
@@ -202,6 +225,14 @@ export class MessageDB {
|
|
|
202
225
|
});
|
|
203
226
|
return read(keys);
|
|
204
227
|
}
|
|
228
|
+
findMessagesByGroupedId(chatId, groupedId) {
|
|
229
|
+
const rows = this.db.prepare(`
|
|
230
|
+
SELECT * FROM messages
|
|
231
|
+
WHERE platform = 'telegram' AND chat_id = ? AND raw_json IS NOT NULL
|
|
232
|
+
ORDER BY msg_id ASC
|
|
233
|
+
`).all(canonicalChatId(chatId));
|
|
234
|
+
return rows.filter((row) => extractGroupedId(row.raw_json) === groupedId);
|
|
235
|
+
}
|
|
205
236
|
getToday(options = {}) {
|
|
206
237
|
const [start, nextStart] = this.todayRange(options.tzOffsetHours);
|
|
207
238
|
const params = [start, nextStart];
|
|
@@ -395,14 +426,19 @@ export class MessageDB {
|
|
|
395
426
|
return { sql: `SELECT * FROM messages WHERE ${conditions.join(' AND ')}`, params };
|
|
396
427
|
}
|
|
397
428
|
insertPrepared(stmt, row) {
|
|
398
|
-
const { preview_jpeg_base64, ...persisted } = row;
|
|
399
|
-
void preview_jpeg_base64;
|
|
400
429
|
return stmt.run({
|
|
401
|
-
...
|
|
430
|
+
...row,
|
|
402
431
|
chat_id: canonicalChatId(row.chat_id),
|
|
403
432
|
raw_json: row.raw_json == null ? null : JSON.stringify(row.raw_json),
|
|
433
|
+
preview_jpeg_base64: row.preview_jpeg_base64 ?? null,
|
|
404
434
|
}).changes;
|
|
405
435
|
}
|
|
436
|
+
ensurePreviewColumn() {
|
|
437
|
+
const columns = this.db.prepare('PRAGMA table_info(messages)').all();
|
|
438
|
+
if (columns.some((column) => column.name === 'preview_jpeg_base64'))
|
|
439
|
+
return;
|
|
440
|
+
this.db.exec('ALTER TABLE messages ADD COLUMN preview_jpeg_base64 TEXT');
|
|
441
|
+
}
|
|
406
442
|
addFilters(conditions, params, options, tableAlias) {
|
|
407
443
|
const prefix = tableAlias ? `${tableAlias}.` : '';
|
|
408
444
|
if (options.chatId != null) {
|