Add Telegram cancellation import functionality with tests and documentation
- 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`.
This commit is contained in:
57
backend/src/services/telegramSource.js
Normal file
57
backend/src/services/telegramSource.js
Normal file
@@ -0,0 +1,57 @@
|
||||
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 };
|
||||
Reference in New Issue
Block a user