- Implemented Telegram cancellation parsing logic in `telegramCancellationParser.js`. - Created unit tests for cancellation parsing in `telegramCancellationParser.test.js`. - Developed integration tests for Telegram import service in `telegramImport.integration.test.js`. - Added API tests for Telegram import endpoints in `telegramImportApi.test.js`. - Introduced service tests for Telegram import logic in `telegramImportService.test.js`. - Documented the Telegram cancellation import process in `telegram-cancellation-import.md`. - Built a React component for managing Telegram imports in `TelegramImportPanel.jsx`.
58 lines
2.6 KiB
JavaScript
58 lines
2.6 KiB
JavaScript
const fs = require('node:fs/promises');
|
|
const { assertExternalSessionFile } = require('../config/telegram');
|
|
|
|
class TelegramSource {
|
|
constructor(config) { this.config = config; this.client = null; this.channel = null; }
|
|
|
|
async connect() {
|
|
if (this.client?.connected && this.channel) return;
|
|
if (this.client) await this.close();
|
|
const file = await assertExternalSessionFile(this.config.sessionFile);
|
|
const session = (await fs.readFile(file, 'utf8')).trim();
|
|
if (!session) throw new Error('SESSION_MISSING');
|
|
const { TelegramClient } = require('teleproto');
|
|
const { StringSession } = require('teleproto/sessions');
|
|
const { Logger } = require('teleproto/extensions/Logger');
|
|
const client = new TelegramClient(new StringSession(session), this.config.apiId, this.config.apiHash, {
|
|
connectionRetries: 2, requestRetries: 2, floodSleepThreshold: 0, baseLogger: new Logger('none')
|
|
});
|
|
this.client = client;
|
|
await client.connect();
|
|
if (!await client.checkAuthorization()) throw new Error('AUTH_REQUIRED');
|
|
if ((await client.getMe()).bot) throw new Error('USER_ACCOUNT_REQUIRED');
|
|
const channel = await client.getEntity(this.config.channel);
|
|
if (channel.className !== 'Channel' || !channel.broadcast || channel.username?.toLowerCase() !== this.config.channel.toLowerCase()) {
|
|
throw new Error('CHANNEL_INVALID');
|
|
}
|
|
this.channel = channel;
|
|
}
|
|
|
|
async fetchMessages(since) {
|
|
await this.connect();
|
|
const messages = [];
|
|
// Pagination revisits recent messages so edits are detected after restarts.
|
|
// The bound prevents an accidental full-history import of a busy channel.
|
|
for await (const message of this.client.iterMessages(this.channel, { limit: 1000, waitTime: 1 })) {
|
|
if (message.date && message.date * 1000 < since.getTime()) return messages.reverse();
|
|
if (message.className !== 'Message' || !message.date) continue;
|
|
messages.push({
|
|
channelId: this.channel.id.toString(), channelUsername: this.channel.username,
|
|
messageId: message.id, text: message.message || '', hasMedia: Boolean(message.media && message.media.className !== 'MessageMediaWebPage'),
|
|
publishedAt: new Date(message.date * 1000).toISOString(),
|
|
editedAt: message.editDate ? new Date(message.editDate * 1000).toISOString() : null
|
|
});
|
|
}
|
|
if (messages.length >= 1000) throw new Error('HISTORY_LIMIT');
|
|
return messages.reverse();
|
|
}
|
|
|
|
async close() {
|
|
const client = this.client;
|
|
this.client = null;
|
|
this.channel = null;
|
|
if (client) await client.destroy();
|
|
}
|
|
}
|
|
|
|
module.exports = { TelegramSource };
|