@imessaging/telegram-mtproto 0.6.4 → 0.6.6
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/package.json
CHANGED
|
@@ -86,6 +86,8 @@ export class TelegramMtprotoTransport
|
|
|
86
86
|
private lastActivityAt?: Date;
|
|
87
87
|
private readonly incoming = new IncomingDispatcher();
|
|
88
88
|
private incomingListener: ((event: unknown) => void) | null = null;
|
|
89
|
+
/** Отметка жизни подписки: срабатывает на любом обновлении, включая служебные. */
|
|
90
|
+
private activityListener: ((event: unknown) => void) | null = null;
|
|
89
91
|
|
|
90
92
|
constructor(private readonly options: TelegramMtprotoTransportOptions) {
|
|
91
93
|
if (!options.accountId.trim()) throw new Error("accountId must not be empty");
|
|
@@ -182,6 +184,17 @@ export class TelegramMtprotoTransport
|
|
|
182
184
|
};
|
|
183
185
|
this.incomingListener = listener;
|
|
184
186
|
this.client.addEventHandler(listener, new NewMessage({}));
|
|
187
|
+
|
|
188
|
+
// ОТМЕТКА ЖИЗНИ ПОДПИСКИ — на ЛЮБОМ обновлении, а не только на сообщении.
|
|
189
|
+
//
|
|
190
|
+
// Канал заказчика молчит и сутки — это его обычный ритм, а не поломка. Сторож, который смотрит
|
|
191
|
+
// на сообщения, в таком затишье перезапускает процесс зря, а каждый перезапуск — новая
|
|
192
|
+
// авторизация сессии, за частоту которых Telegram наказывает. Служебные события идут и в
|
|
193
|
+
// тишине, поэтому именно они отличают мёртвую подписку от спокойного канала.
|
|
194
|
+
this.activityListener = (): void => {
|
|
195
|
+
this.lastActivityAt = new Date();
|
|
196
|
+
};
|
|
197
|
+
this.client.addEventHandler(this.activityListener);
|
|
185
198
|
}
|
|
186
199
|
|
|
187
200
|
/**
|
|
@@ -218,10 +231,50 @@ export class TelegramMtprotoTransport
|
|
|
218
231
|
}
|
|
219
232
|
}
|
|
220
233
|
|
|
234
|
+
/**
|
|
235
|
+
* ДОГРУЗКА ПРОПУЩЕННОГО. Возвращает число сообщений, отданных подписчикам.
|
|
236
|
+
*
|
|
237
|
+
* Живая подписка теряет часть сообщений — чаще всего в супергруппах с темами: обновление
|
|
238
|
+
* приходит по каналу, которого клиент не помнит, и молча отбрасывается. Замер 07.09.2026:
|
|
239
|
+
* сообщение в теме форума видно по ссылке, а в подписке его нет ни у одного потребителя.
|
|
240
|
+
* Продукт italent живёт с этим давно и добирает последние сообщения опросом — здесь то же
|
|
241
|
+
* самое, только на уровне транспорта, чтобы каждому приложению не изобретать своё.
|
|
242
|
+
*
|
|
243
|
+
* Повторы неизбежны и это нормально: потребитель обязан отсеивать по идентификатору сообщения,
|
|
244
|
+
* а не полагаться на то, что канал не повторится. Он и так повторяется в обычной жизни.
|
|
245
|
+
*/
|
|
246
|
+
async catchUp(options: { limit?: number; dialogs?: number } = {}): Promise<number> {
|
|
247
|
+
const limit = options.limit ?? 20;
|
|
248
|
+
const dialogs = await this.client.getDialogs({ limit: options.dialogs ?? 200 });
|
|
249
|
+
let dispatched = 0;
|
|
250
|
+
for (const dialog of dialogs) {
|
|
251
|
+
if (!dialog.isGroup && !dialog.isChannel) continue;
|
|
252
|
+
const entity = dialog.entity;
|
|
253
|
+
if (!entity) continue;
|
|
254
|
+
const messages = await this.client.getMessages(entity, { limit }).catch(() => []);
|
|
255
|
+
// От старых к новым: порядок обработки должен совпадать с тем, в каком они пришли бы живьём.
|
|
256
|
+
for (const message of [...messages].reverse()) {
|
|
257
|
+
const delivered = await this.incoming.dispatch(
|
|
258
|
+
message as unknown as MtprotoMessageLike,
|
|
259
|
+
this.id,
|
|
260
|
+
this.options.accountId,
|
|
261
|
+
);
|
|
262
|
+
if (delivered) dispatched += 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
this.lastActivityAt = new Date();
|
|
266
|
+
return dispatched;
|
|
267
|
+
}
|
|
268
|
+
|
|
221
269
|
private stopListeningIfIdle(): void {
|
|
222
270
|
if (this.incoming.size > 0 || this.incomingListener === null) return;
|
|
223
271
|
this.client.removeEventHandler(this.incomingListener, new NewMessage({}));
|
|
224
272
|
this.incomingListener = null;
|
|
273
|
+
if (this.activityListener) {
|
|
274
|
+
// Снимается тем же способом, что и ставилась: без построителя событий.
|
|
275
|
+
this.client.removeEventHandler(this.activityListener, undefined as never);
|
|
276
|
+
this.activityListener = null;
|
|
277
|
+
}
|
|
225
278
|
}
|
|
226
279
|
|
|
227
280
|
/**
|