fix: various fixes, improved updates handing

This commit is contained in:
teidesu 2021-08-05 20:14:19 +03:00
parent 8504e3bf14
commit b7751f0a57
6 changed files with 45 additions and 19 deletions

View file

@ -149,6 +149,7 @@ import {
_dispatchUpdate, _dispatchUpdate,
_fetchUpdatesState, _fetchUpdatesState,
_handleUpdate, _handleUpdate,
_keepAliveAction,
_loadStorage, _loadStorage,
_saveStorage, _saveStorage,
catchUp, catchUp,
@ -172,6 +173,7 @@ import { Readable } from 'stream'
import { import {
ArrayWithTotal, ArrayWithTotal,
BotCommands, BotCommands,
BotStoppedUpdate,
CallbackQuery, CallbackQuery,
Chat, Chat,
ChatEvent, ChatEvent,
@ -339,6 +341,13 @@ export interface TelegramClient extends BaseTelegramClient {
* @param handler History read handler * @param handler History read handler
*/ */
on(name: 'history_read', handler: (upd: HistoryReadUpdate) => void): this on(name: 'history_read', handler: (upd: HistoryReadUpdate) => void): this
/**
* Register a bot stopped handler
*
* @param name Event name
* @param handler Bot stopped handler
*/
on(name: 'bot_stopped', handler: (upd: BotStoppedUpdate) => void): this
/** /**
* Accept the given TOS * Accept the given TOS
* *
@ -3660,6 +3669,7 @@ export class TelegramClient extends BaseTelegramClient {
protected _dispatchUpdate = _dispatchUpdate protected _dispatchUpdate = _dispatchUpdate
_handleUpdate = _handleUpdate _handleUpdate = _handleUpdate
catchUp = catchUp catchUp = catchUp
protected _keepAliveAction = _keepAliveAction
blockUser = blockUser blockUser = blockUser
deleteProfilePhotos = deleteProfilePhotos deleteProfilePhotos = deleteProfilePhotos
getCommonChats = getCommonChats getCommonChats = getCommonChats

View file

@ -52,7 +52,8 @@ import {
PollVoteUpdate, PollVoteUpdate,
UserStatusUpdate, UserStatusUpdate,
UserTypingUpdate, UserTypingUpdate,
Conversation Conversation,
BotStoppedUpdate
} from '../types' } from '../types'
// @copy // @copy

View file

@ -10,7 +10,7 @@ import {
getBarePeerId, getBarePeerId,
getMarkedPeerId, getMarkedPeerId,
markedPeerIdToBare, markedPeerIdToBare,
MAX_CHANNEL_ID, MAX_CHANNEL_ID, RpcError,
} from '@mtqt/core' } from '@mtqt/core'
import { isDummyUpdate, isDummyUpdates } from '../utils/updates-utils' import { isDummyUpdate, isDummyUpdates } from '../utils/updates-utils'
import { ChatsIndex, UsersIndex } from '../types' import { ChatsIndex, UsersIndex } from '../types'
@ -1002,3 +1002,9 @@ export function catchUp(this: TelegramClient): Promise<void> {
.then(() => this._updLock.release()) .then(() => this._updLock.release())
.then(() => this._saveStorage()) .then(() => this._saveStorage())
} }
/** @internal */
export function _keepAliveAction(this: TelegramClient): void {
debug('no updates for >15 minutes, catching up')
this.catchUp().catch((err) => this._emitError(err))
}

View file

@ -236,7 +236,7 @@ export class BaseTelegramClient extends EventEmitter {
readonly _layer: number readonly _layer: number
private _keepAliveInterval?: NodeJS.Timeout private _keepAliveInterval?: NodeJS.Timeout
private _lastRequestTime = 0 protected _lastUpdateTime = 0
private _floodWaitedRequests: Record<string, number> = {} private _floodWaitedRequests: Record<string, number> = {}
protected _config?: tl.RawConfig protected _config?: tl.RawConfig
@ -332,6 +332,19 @@ export class BaseTelegramClient extends EventEmitter {
await this.storage.save?.() await this.storage.save?.()
} }
protected _keepAliveAction(): void {
// telegram asks to fetch pending updates
// if there are no updates for 15 minutes.
// core does not have update handling,
// so we just use getState so the server knows
// we still do need updates
this.call({ _: 'updates.getState' }).catch((e) => {
if (!(e instanceof RpcError)) {
this.primaryConnection.reconnect()
}
})
}
private _cleanupPrimaryConnection(forever = false): void { private _cleanupPrimaryConnection(forever = false): void {
if (forever && this.primaryConnection) this.primaryConnection.destroy() if (forever && this.primaryConnection) this.primaryConnection.destroy()
if (this._keepAliveInterval) clearInterval(this._keepAliveInterval) if (this._keepAliveInterval) clearInterval(this._keepAliveInterval)
@ -349,25 +362,19 @@ export class BaseTelegramClient extends EventEmitter {
reconnectionStrategy: this._reconnectionStrategy, reconnectionStrategy: this._reconnectionStrategy,
layer: this._layer, layer: this._layer,
}) })
this.primaryConnection.on('usable', async () => { this.primaryConnection.on('usable', async (isReconnection: boolean) => {
this._lastUpdateTime = Date.now()
this._keepAliveInterval = setInterval(async () => { this._keepAliveInterval = setInterval(async () => {
// according to telethon, "We need to send some content-related request at least hourly if (Date.now() - this._lastUpdateTime > 900_000) {
// for Telegram to keep delivering updates, otherwise they will just stop even if we're connected. this._keepAliveAction()
// Do so every 30 minutes" this._lastUpdateTime = Date.now()
if (Date.now() - this._lastRequestTime > 1800_000) {
try {
await this.call({ _: 'updates.getState' })
} catch (e) {
if (!(e instanceof RpcError)) {
this.primaryConnection.reconnect()
}
}
} }
}, 60_000) }, 60_000)
// on reconnection we need to call updates.getState so Telegram // on reconnection we need to call updates.getState so Telegram
// knows we still want the updates // knows we still want the updates
if (!this._disableUpdates) { if (isReconnection && !this._disableUpdates) {
setTimeout(async () => { setTimeout(async () => {
try { try {
await this.call({ _: 'updates.getState' }) await this.call({ _: 'updates.getState' })
@ -380,6 +387,7 @@ export class BaseTelegramClient extends EventEmitter {
} }
}) })
this.primaryConnection.on('update', (update) => { this.primaryConnection.on('update', (update) => {
this._lastUpdateTime = Date.now()
this._handleUpdate(update) this._handleUpdate(update)
}) })
this.primaryConnection.on('wait', () => this.primaryConnection.on('wait', () =>
@ -568,8 +576,6 @@ export class BaseTelegramClient extends EventEmitter {
} as any // who cares } as any // who cares
} }
this._lastRequestTime = Date.now()
const connection = params?.connection ?? this.primaryConnection const connection = params?.connection ?? this.primaryConnection
let lastError: Error | null = null let lastError: Error | null = null

View file

@ -82,12 +82,14 @@ export abstract class PersistentConnection extends EventEmitter {
} }
protected onConnectionUsable(): void { protected onConnectionUsable(): void {
const isReconnection = this._consequentFails > 0
// reset reconnection related state // reset reconnection related state
this._lastError = null this._lastError = null
this._consequentFails = 0 this._consequentFails = 0
this._previousWait = null this._previousWait = null
this._usable = true this._usable = true
this.emit('usable') this.emit('usable', isReconnection)
this._rescheduleInactivity() this._rescheduleInactivity()
} }

View file

@ -149,6 +149,7 @@ export class TelegramConnection extends PersistentConnection {
it.promise.reject(new Error('Connection destroyed')) it.promise.reject(new Error('Connection destroyed'))
) )
this._mtproto.reset() this._mtproto.reset()
this.removeAllListeners()
} }
} }