fix(client): better updates handling, also emit updates returned by RPC methods
This commit is contained in:
parent
58f4356d58
commit
c863e7a854
22 changed files with 265 additions and 39 deletions
|
@ -1545,6 +1545,9 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
* Also note that entity maps may contain entities that are not
|
* Also note that entity maps may contain entities that are not
|
||||||
* used in this particular update, so do not rely on its contents.
|
* used in this particular update, so do not rely on its contents.
|
||||||
*
|
*
|
||||||
|
* `update` might contain a Message object - in this case,
|
||||||
|
* it should be interpreted as some kind of `updateNewMessage`.
|
||||||
|
*
|
||||||
* @param update Update that has just happened
|
* @param update Update that has just happened
|
||||||
* @param users Map of users in this update
|
* @param users Map of users in this update
|
||||||
* @param chats Map of chats in this update
|
* @param chats Map of chats in this update
|
||||||
|
|
|
@ -34,15 +34,16 @@ export async function addChatMembers(
|
||||||
const p = normalizeToInputUser(await this.resolvePeer(user))
|
const p = normalizeToInputUser(await this.resolvePeer(user))
|
||||||
if (!p) continue
|
if (!p) continue
|
||||||
|
|
||||||
await this.call({
|
const updates = await this.call({
|
||||||
_: 'messages.addChatUser',
|
_: 'messages.addChatUser',
|
||||||
chatId: input.chatId,
|
chatId: input.chatId,
|
||||||
userId: p,
|
userId: p,
|
||||||
fwdLimit: forwardCount,
|
fwdLimit: forwardCount,
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(updates)
|
||||||
}
|
}
|
||||||
} else if (input._ === 'inputPeerChannel') {
|
} else if (input._ === 'inputPeerChannel') {
|
||||||
await this.call({
|
const updates = await this.call({
|
||||||
_: 'channels.inviteToChannel',
|
_: 'channels.inviteToChannel',
|
||||||
channel: normalizeToInputChannel(chat)!,
|
channel: normalizeToInputChannel(chat)!,
|
||||||
users: await Promise.all(
|
users: await Promise.all(
|
||||||
|
@ -52,5 +53,6 @@ export async function addChatMembers(
|
||||||
).then((res) => res.filter(Boolean)) as tl.TypeInputUser[],
|
).then((res) => res.filter(Boolean)) as tl.TypeInputUser[],
|
||||||
fwdLimit: forwardCount,
|
fwdLimit: forwardCount,
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(updates)
|
||||||
} else throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
} else throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,9 @@ export async function archiveChats(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.call({
|
const updates = await this.call({
|
||||||
_: 'folders.editPeerFolders',
|
_: 'folders.editPeerFolders',
|
||||||
folderPeers
|
folderPeers
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(updates)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,7 @@ export async function createChannel(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
return new Chat(this, res.chats[0])
|
return new Chat(this, res.chats[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,5 +42,7 @@ export async function createGroup(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
return new Chat(this, res.chats[0])
|
return new Chat(this, res.chats[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,5 +28,7 @@ export async function createSupergroup(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
return new Chat(this, res.chats[0])
|
return new Chat(this, res.chats[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,9 @@ export async function deleteChannel(this: TelegramClient, chatId: InputPeerLike)
|
||||||
const peer = normalizeToInputChannel(await this.resolvePeer(chatId))
|
const peer = normalizeToInputChannel(await this.resolvePeer(chatId))
|
||||||
if (!peer) throw new MtCuteInvalidPeerTypeError(chatId, 'channel')
|
if (!peer) throw new MtCuteInvalidPeerTypeError(chatId, 'channel')
|
||||||
|
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.deleteChannel',
|
_: 'channels.deleteChannel',
|
||||||
channel: peer
|
channel: peer
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,17 +21,20 @@ export async function deleteChatPhoto(
|
||||||
if (!(chat._ === 'inputPeerChat' || chat._ === 'inputPeerChannel'))
|
if (!(chat._ === 'inputPeerChat' || chat._ === 'inputPeerChannel'))
|
||||||
throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
||||||
|
|
||||||
|
let res
|
||||||
if (chat._ === 'inputPeerChat') {
|
if (chat._ === 'inputPeerChat') {
|
||||||
await this.call({
|
res = await this.call({
|
||||||
_: 'messages.editChatPhoto',
|
_: 'messages.editChatPhoto',
|
||||||
chatId: chat.chatId,
|
chatId: chat.chatId,
|
||||||
photo: { _: 'inputChatPhotoEmpty' }
|
photo: { _: 'inputChatPhotoEmpty' }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await this.call({
|
res = await this.call({
|
||||||
_: 'channels.editPhoto',
|
_: 'channels.editPhoto',
|
||||||
channel: normalizeToInputChannel(chat)!,
|
channel: normalizeToInputChannel(chat)!,
|
||||||
photo: { _: 'inputChatPhotoEmpty' }
|
photo: { _: 'inputChatPhotoEmpty' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,13 @@ export async function deleteGroup(
|
||||||
if (chat._ !== 'inputPeerChat')
|
if (chat._ !== 'inputPeerChat')
|
||||||
throw new MtCuteInvalidPeerTypeError(chatId, 'chat')
|
throw new MtCuteInvalidPeerTypeError(chatId, 'chat')
|
||||||
|
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.deleteChatUser',
|
_: 'messages.deleteChatUser',
|
||||||
revokeHistory: true,
|
revokeHistory: true,
|
||||||
chatId: chat.chatId,
|
chatId: chat.chatId,
|
||||||
userId: { _: 'inputUserSelf' },
|
userId: { _: 'inputUserSelf' },
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
await this.call({
|
await this.call({
|
||||||
_: 'messages.deleteChat',
|
_: 'messages.deleteChat',
|
||||||
|
|
|
@ -5,7 +5,10 @@ import {
|
||||||
MtCuteNotFoundError,
|
MtCuteNotFoundError,
|
||||||
MtCuteTypeAssertionError,
|
MtCuteTypeAssertionError,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
import { INVITE_LINK_REGEX, normalizeToInputChannel } from '../../utils/peer-utils'
|
import {
|
||||||
|
INVITE_LINK_REGEX,
|
||||||
|
normalizeToInputChannel,
|
||||||
|
} from '../../utils/peer-utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join a channel or supergroup
|
* Join a channel or supergroup
|
||||||
|
@ -34,6 +37,8 @@ export async function joinChat(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
return new Chat(this, res.chats[0])
|
return new Chat(this, res.chats[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,5 +58,7 @@ export async function joinChat(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
return new Chat(this, res.chats[0])
|
return new Chat(this, res.chats[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,18 @@ export async function leaveChat(
|
||||||
const input = normalizeToInputPeer(chat)
|
const input = normalizeToInputPeer(chat)
|
||||||
|
|
||||||
if (input._ === 'inputPeerChannel') {
|
if (input._ === 'inputPeerChannel') {
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.leaveChannel',
|
_: 'channels.leaveChannel',
|
||||||
channel: normalizeToInputChannel(chat)!,
|
channel: normalizeToInputChannel(chat)!,
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
} else if (input._ === 'inputPeerChat') {
|
} else if (input._ === 'inputPeerChat') {
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.deleteChatUser',
|
_: 'messages.deleteChatUser',
|
||||||
chatId: input.chatId,
|
chatId: input.chatId,
|
||||||
userId: { _: 'inputUserSelf' },
|
userId: { _: 'inputUserSelf' },
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
if (clear) {
|
if (clear) {
|
||||||
await this.deleteHistory(input)
|
await this.deleteHistory(input)
|
||||||
|
|
|
@ -59,5 +59,7 @@ export async function setChatDefaultPermissions(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
|
|
||||||
return new Chat(this, res.chats[0])
|
return new Chat(this, res.chats[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ export async function setChatPhoto(
|
||||||
|
|
||||||
if (typeof media === 'string' && media.match(/^https?:\/\//)) {
|
if (typeof media === 'string' && media.match(/^https?:\/\//)) {
|
||||||
throw new MtCuteArgumentError("Chat photo can't be external")
|
throw new MtCuteArgumentError("Chat photo can't be external")
|
||||||
|
} else if (typeof media === 'object' && tl.isAnyInputMedia(media)) {
|
||||||
|
throw new MtCuteArgumentError("Chat photo can't be InputMedia")
|
||||||
} else if (isUploadedFile(media)) {
|
} else if (isUploadedFile(media)) {
|
||||||
input = media.inputFile
|
input = media.inputFile
|
||||||
} else if (typeof media === 'object' && tl.isAnyInputFile(media)) {
|
} else if (typeof media === 'object' && tl.isAnyInputFile(media)) {
|
||||||
|
@ -54,17 +56,19 @@ export async function setChatPhoto(
|
||||||
videoStartTs: previewSec
|
videoStartTs: previewSec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let res
|
||||||
if (chat._ === 'inputPeerChat') {
|
if (chat._ === 'inputPeerChat') {
|
||||||
await this.call({
|
res = await this.call({
|
||||||
_: 'messages.editChatPhoto',
|
_: 'messages.editChatPhoto',
|
||||||
chatId: chat.chatId,
|
chatId: chat.chatId,
|
||||||
photo
|
photo
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await this.call({
|
res = await this.call({
|
||||||
_: 'channels.editPhoto',
|
_: 'channels.editPhoto',
|
||||||
channel: normalizeToInputChannel(chat)!,
|
channel: normalizeToInputChannel(chat)!,
|
||||||
photo
|
photo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,17 +23,19 @@ export async function setChatTitle(
|
||||||
if (!(chat._ === 'inputPeerChat' || chat._ === 'inputPeerChannel'))
|
if (!(chat._ === 'inputPeerChat' || chat._ === 'inputPeerChannel'))
|
||||||
throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
|
||||||
|
|
||||||
|
let res
|
||||||
if (chat._ === 'inputPeerChat') {
|
if (chat._ === 'inputPeerChat') {
|
||||||
await this.call({
|
res = await this.call({
|
||||||
_: 'messages.editChatTitle',
|
_: 'messages.editChatTitle',
|
||||||
chatId: chat.chatId,
|
chatId: chat.chatId,
|
||||||
title
|
title
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await this.call({
|
res = await this.call({
|
||||||
_: 'channels.editTitle',
|
_: 'channels.editTitle',
|
||||||
channel: normalizeToInputChannel(chat)!,
|
channel: normalizeToInputChannel(chat)!,
|
||||||
title
|
title
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,10 @@ export async function setSlowMode(
|
||||||
const chat = normalizeToInputChannel(await this.resolvePeer(chatId))
|
const chat = normalizeToInputChannel(await this.resolvePeer(chatId))
|
||||||
if (!chat) throw new MtCuteInvalidPeerTypeError(chatId, 'channel')
|
if (!chat) throw new MtCuteInvalidPeerTypeError(chatId, 'channel')
|
||||||
|
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.toggleSlowMode',
|
_: 'channels.toggleSlowMode',
|
||||||
channel: chat,
|
channel: chat,
|
||||||
seconds
|
seconds
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,9 @@ export async function unarchiveChats(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'folders.editPeerFolders',
|
_: 'folders.editPeerFolders',
|
||||||
folderPeers
|
folderPeers
|
||||||
})
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,5 +37,7 @@ export async function deleteMessages(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._pts = res.pts
|
||||||
|
|
||||||
return !!res.ptsCount
|
return !!res.ptsCount
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ export function _findMessageInUpdate(
|
||||||
res._
|
res._
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this._handleUpdate(res, true)
|
||||||
|
|
||||||
for (const u of res.updates) {
|
for (const u of res.updates) {
|
||||||
if (
|
if (
|
||||||
isEdit && (
|
isEdit && (
|
||||||
|
|
|
@ -21,11 +21,13 @@ export async function pinMessage(
|
||||||
notify = false,
|
notify = false,
|
||||||
bothSides = false
|
bothSides = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.updatePinnedMessage',
|
_: 'messages.updatePinnedMessage',
|
||||||
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
|
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
|
||||||
id: messageId,
|
id: messageId,
|
||||||
silent: !notify,
|
silent: !notify,
|
||||||
pmOneside: !bothSides
|
pmOneside: !bothSides
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,9 @@ export async function sendText(
|
||||||
entities: res.entities,
|
entities: res.entities,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._pts = res.pts
|
||||||
|
this._date = res.date
|
||||||
|
|
||||||
return new Message(this, msg, {}, {})
|
return new Message(this, msg, {}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,12 @@ export async function unpinMessage(
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
messageId: number,
|
messageId: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.updatePinnedMessage',
|
_: 'messages.updatePinnedMessage',
|
||||||
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
|
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
|
||||||
id: messageId,
|
id: messageId,
|
||||||
unpin: true
|
unpin: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this._handleUpdate(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ const debug = require('debug')('mtcute:upds')
|
||||||
// tldr server sends multiple `updates` with the same seq, and that seq
|
// tldr server sends multiple `updates` with the same seq, and that seq
|
||||||
// is also larger than the seq in the initial updates.getState response
|
// is also larger than the seq in the initial updates.getState response
|
||||||
|
|
||||||
|
// also code in this file is very bad, thanks to Telegram's awesome updates mechanism
|
||||||
|
|
||||||
// @extension
|
// @extension
|
||||||
interface UpdatesState {
|
interface UpdatesState {
|
||||||
_updLock: Lock
|
_updLock: Lock
|
||||||
|
@ -134,7 +136,64 @@ export function dispatchUpdate(
|
||||||
// no-op //
|
// no-op //
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _loadDifference(this: TelegramClient): Promise<void> {
|
interface NoDispatchIndex {
|
||||||
|
// channel id or 0 => msg id
|
||||||
|
msg: Record<number, Record<number, true>>
|
||||||
|
// channel id or 0 => pts
|
||||||
|
pts: Record<number, Record<number, true>>
|
||||||
|
}
|
||||||
|
|
||||||
|
// creating and using a no-dispatch index is pretty expensive,
|
||||||
|
// but its not a big deal since it's actually rarely needed
|
||||||
|
function _createNoDispatchIndex(
|
||||||
|
updates?: tl.TypeUpdates
|
||||||
|
): NoDispatchIndex | undefined {
|
||||||
|
if (!updates) return undefined
|
||||||
|
const ret: NoDispatchIndex = {
|
||||||
|
msg: {},
|
||||||
|
pts: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updates._ === 'updates' || updates._ === 'updatesCombined') {
|
||||||
|
updates.updates.forEach((upd) => {
|
||||||
|
const cid = extractChannelIdFromUpdate(upd) ?? 0
|
||||||
|
if (
|
||||||
|
upd._ === 'updateNewMessage' ||
|
||||||
|
upd._ === 'updateNewChannelMessage'
|
||||||
|
) {
|
||||||
|
if (!ret.msg[cid]) ret.msg[cid] = {}
|
||||||
|
ret.msg[cid][upd.message.id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const pts = 'pts' in upd ? upd.pts : undefined
|
||||||
|
|
||||||
|
if (pts) {
|
||||||
|
if (!ret.msg[cid]) ret.msg[cid] = {}
|
||||||
|
ret.msg[cid][pts] = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
updates._ === 'updateShortMessage' ||
|
||||||
|
updates._ === 'updateShortChatMessage' ||
|
||||||
|
updates._ === 'updateShortSentMessage'
|
||||||
|
) {
|
||||||
|
// these updates are only used for non-channel messages, so we use 0
|
||||||
|
if (!ret.msg[0]) ret.msg[0] = {}
|
||||||
|
if (!ret.pts[0]) ret.pts[0] = {}
|
||||||
|
|
||||||
|
ret.msg[0][updates.id] = true
|
||||||
|
ret.pts[0][updates.pts] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _loadDifference(
|
||||||
|
this: TelegramClient,
|
||||||
|
noDispatch?: NoDispatchIndex
|
||||||
|
): Promise<void> {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const diff = await this.call({
|
const diff = await this.call({
|
||||||
_: 'updates.getDifference',
|
_: 'updates.getDifference',
|
||||||
|
@ -158,12 +217,59 @@ async function _loadDifference(this: TelegramClient): Promise<void> {
|
||||||
|
|
||||||
const { users, chats } = createUsersChatsIndex(diff)
|
const { users, chats } = createUsersChatsIndex(diff)
|
||||||
|
|
||||||
diff.newMessages.forEach((message) =>
|
diff.newMessages.forEach((message) => {
|
||||||
|
if (noDispatch) {
|
||||||
|
// in fact this field seems to only be used for PMs and legacy chats,
|
||||||
|
// so `cid` will be 0 always, but that might change :shrug:
|
||||||
|
const cid =
|
||||||
|
message.peerId?._ === 'peerChannel'
|
||||||
|
? message.peerId.channelId
|
||||||
|
: 0
|
||||||
|
if (noDispatch.msg[cid][message.id]) return
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatchUpdate(message, users, chats)
|
this.dispatchUpdate(message, users, chats)
|
||||||
)
|
})
|
||||||
diff.otherUpdates.forEach((upd) =>
|
|
||||||
|
for (const upd of diff.otherUpdates) {
|
||||||
|
const cid = extractChannelIdFromUpdate(upd)
|
||||||
|
const pts = 'pts' in upd ? upd.pts : undefined
|
||||||
|
const ptsCount = 'ptsCount' in upd ? upd.ptsCount : undefined
|
||||||
|
|
||||||
|
if (cid && pts !== undefined && ptsCount !== undefined) {
|
||||||
|
// check that this pts is in fact the next one
|
||||||
|
// we only need to check this for channels since for
|
||||||
|
// common pts it is guaranteed by the server
|
||||||
|
// (however i would not really trust telegram server lol)
|
||||||
|
let nextLocalPts
|
||||||
|
if (cid in this._cpts) nextLocalPts = this._cpts[cid] + ptsCount
|
||||||
|
else {
|
||||||
|
const saved = await this.storage.getChannelPts(cid)
|
||||||
|
if (saved) {
|
||||||
|
this._cpts[cid] = saved
|
||||||
|
nextLocalPts = saved + ptsCount
|
||||||
|
} else {
|
||||||
|
nextLocalPts = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextLocalPts) {
|
||||||
|
if (nextLocalPts > pts) continue
|
||||||
|
if (nextLocalPts < pts) {
|
||||||
|
await _loadChannelDifference.call(this, cid, noDispatch)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cpts[cid] = pts
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noDispatch && pts) {
|
||||||
|
if (noDispatch.pts[cid ?? 0][pts]) continue
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatchUpdate(upd, users, chats)
|
this.dispatchUpdate(upd, users, chats)
|
||||||
)
|
}
|
||||||
|
|
||||||
this._pts = state.pts
|
this._pts = state.pts
|
||||||
this._date = state.date
|
this._date = state.date
|
||||||
|
@ -174,7 +280,8 @@ async function _loadDifference(this: TelegramClient): Promise<void> {
|
||||||
|
|
||||||
async function _loadChannelDifference(
|
async function _loadChannelDifference(
|
||||||
this: TelegramClient,
|
this: TelegramClient,
|
||||||
channelId: number
|
channelId: number,
|
||||||
|
noDispatch?: NoDispatchIndex
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let channel
|
let channel
|
||||||
try {
|
try {
|
||||||
|
@ -209,17 +316,32 @@ async function _loadChannelDifference(
|
||||||
|
|
||||||
const { users, chats } = createUsersChatsIndex(diff)
|
const { users, chats } = createUsersChatsIndex(diff)
|
||||||
|
|
||||||
diff.newMessages.forEach((message) =>
|
diff.newMessages.forEach((message) => {
|
||||||
|
if (noDispatch && noDispatch.msg[channelId][message.id]) return
|
||||||
|
|
||||||
this.dispatchUpdate(message, users, chats)
|
this.dispatchUpdate(message, users, chats)
|
||||||
)
|
})
|
||||||
diff.otherUpdates.forEach((upd) =>
|
|
||||||
|
diff.otherUpdates.forEach((upd) => {
|
||||||
|
if (noDispatch) {
|
||||||
|
const pts = 'pts' in upd ? upd.pts : undefined
|
||||||
|
|
||||||
|
// we don't check for pts sequence here since the server
|
||||||
|
// is expected to return them in a correct order
|
||||||
|
// again, i would not trust Telegram server that much,
|
||||||
|
// but checking pts here seems like an overkill
|
||||||
|
if (pts && noDispatch.pts[channelId][pts]) return
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatchUpdate(upd, users, chats)
|
this.dispatchUpdate(upd, users, chats)
|
||||||
)
|
})
|
||||||
|
|
||||||
pts = diff.pts
|
pts = diff.pts
|
||||||
|
|
||||||
if (diff.final) break
|
if (diff.final) break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._cpts[channelId] = pts
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,7 +349,8 @@ async function _loadChannelDifference(
|
||||||
*/
|
*/
|
||||||
export function _handleUpdate(
|
export function _handleUpdate(
|
||||||
this: TelegramClient,
|
this: TelegramClient,
|
||||||
update: tl.TypeUpdates
|
update: tl.TypeUpdates,
|
||||||
|
noDispatch = false
|
||||||
): void {
|
): void {
|
||||||
// just in case, check that updates state is available
|
// just in case, check that updates state is available
|
||||||
if (this._pts === undefined) {
|
if (this._pts === undefined) {
|
||||||
|
@ -243,6 +366,8 @@ export function _handleUpdate(
|
||||||
// additionally, locking here blocks updates handling while we are
|
// additionally, locking here blocks updates handling while we are
|
||||||
// loading difference inside update handler.
|
// loading difference inside update handler.
|
||||||
|
|
||||||
|
const noDispatchIndex = noDispatch ? _createNoDispatchIndex(update) : undefined
|
||||||
|
|
||||||
this._updLock
|
this._updLock
|
||||||
.acquire()
|
.acquire()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
|
@ -253,7 +378,7 @@ export function _handleUpdate(
|
||||||
// reference: https://core.telegram.org/api/updates
|
// reference: https://core.telegram.org/api/updates
|
||||||
if (update._ === 'updatesTooLong') {
|
if (update._ === 'updatesTooLong') {
|
||||||
// "there are too many events pending to be pushed to the client", we need to fetch them manually
|
// "there are too many events pending to be pushed to the client", we need to fetch them manually
|
||||||
await _loadDifference.call(this)
|
await _loadDifference.call(this, noDispatchIndex)
|
||||||
} else if (
|
} else if (
|
||||||
update._ === 'updates' ||
|
update._ === 'updates' ||
|
||||||
update._ === 'updatesCombined'
|
update._ === 'updatesCombined'
|
||||||
|
@ -285,7 +410,8 @@ export function _handleUpdate(
|
||||||
}
|
}
|
||||||
return await _loadChannelDifference.call(
|
return await _loadChannelDifference.call(
|
||||||
this,
|
this,
|
||||||
upd.channelId
|
upd.channelId,
|
||||||
|
noDispatchIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,21 +450,24 @@ export function _handleUpdate(
|
||||||
// to bother handling them further.
|
// to bother handling them further.
|
||||||
return await _loadChannelDifference.call(
|
return await _loadChannelDifference.call(
|
||||||
this,
|
this,
|
||||||
channelId
|
channelId,
|
||||||
|
noDispatchIndex
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return await _loadDifference.call(this)
|
return await _loadDifference.call(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!noDispatch) {
|
||||||
this.dispatchUpdate(upd, users, chats)
|
this.dispatchUpdate(upd, users, chats)
|
||||||
|
}
|
||||||
|
|
||||||
if (channelId) {
|
if (channelId) {
|
||||||
this._cpts[channelId] = pts
|
this._cpts[channelId] = pts
|
||||||
} else {
|
} else {
|
||||||
this._pts = pts
|
this._pts = pts
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!noDispatch) {
|
||||||
this.dispatchUpdate(upd, users, chats)
|
this.dispatchUpdate(upd, users, chats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,12 +481,14 @@ export function _handleUpdate(
|
||||||
upd.dcOptions
|
upd.dcOptions
|
||||||
} else if (upd._ === 'updateConfig') {
|
} else if (upd._ === 'updateConfig') {
|
||||||
this._config = await this.call({ _: 'help.getConfig' })
|
this._config = await this.call({ _: 'help.getConfig' })
|
||||||
} else {
|
} else if (!noDispatch) {
|
||||||
this.dispatchUpdate(upd, {}, {})
|
this.dispatchUpdate(upd, {}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
this._date = update.date
|
this._date = update.date
|
||||||
} else if (update._ === 'updateShortMessage') {
|
} else if (update._ === 'updateShortMessage') {
|
||||||
|
if (noDispatch) return
|
||||||
|
|
||||||
const message: tl.RawMessage = {
|
const message: tl.RawMessage = {
|
||||||
_: 'message',
|
_: 'message',
|
||||||
out: update.out,
|
out: update.out,
|
||||||
|
@ -412,7 +543,7 @@ export function _handleUpdate(
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (rawUsers.length < 2) {
|
if (rawUsers.length !== id.length) {
|
||||||
// other user failed to load.
|
// other user failed to load.
|
||||||
// first try checking for input peer in storage
|
// first try checking for input peer in storage
|
||||||
const saved = await this.storage.getPeerById(
|
const saved = await this.storage.getPeerById(
|
||||||
|
@ -427,7 +558,7 @@ export function _handleUpdate(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rawUsers.length < 2) {
|
if (rawUsers.length !== id.length) {
|
||||||
// not saved (or invalid hash), not found by id
|
// not saved (or invalid hash), not found by id
|
||||||
// find that user in dialogs (since the update
|
// find that user in dialogs (since the update
|
||||||
// is about an incoming message, dialog with that
|
// is about an incoming message, dialog with that
|
||||||
|
@ -473,6 +604,8 @@ export function _handleUpdate(
|
||||||
})
|
})
|
||||||
this.dispatchUpdate(message, users, chats)
|
this.dispatchUpdate(message, users, chats)
|
||||||
} else if (update._ === 'updateShortChatMessage') {
|
} else if (update._ === 'updateShortChatMessage') {
|
||||||
|
if (noDispatch) return
|
||||||
|
|
||||||
const message: tl.RawMessage = {
|
const message: tl.RawMessage = {
|
||||||
_: 'message',
|
_: 'message',
|
||||||
out: update.out,
|
out: update.out,
|
||||||
|
@ -506,7 +639,6 @@ export function _handleUpdate(
|
||||||
let rawUsers: tl.TypeUser[]
|
let rawUsers: tl.TypeUser[]
|
||||||
{
|
{
|
||||||
const id: tl.TypeInputUser[] = [
|
const id: tl.TypeInputUser[] = [
|
||||||
{ _: 'inputUserSelf' },
|
|
||||||
{
|
{
|
||||||
_: 'inputUser',
|
_: 'inputUser',
|
||||||
userId: update.fromId,
|
userId: update.fromId,
|
||||||
|
@ -523,6 +655,41 @@ export function _handleUpdate(
|
||||||
_: 'users.getUsers',
|
_: 'users.getUsers',
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (rawUsers.length !== id.length) {
|
||||||
|
// user failed to load.
|
||||||
|
// first try checking for input peer in storage
|
||||||
|
const saved = await this.storage.getPeerById(
|
||||||
|
update.fromId
|
||||||
|
)
|
||||||
|
if (saved) {
|
||||||
|
id[0] = normalizeToInputUser(saved)!
|
||||||
|
|
||||||
|
rawUsers = await this.call({
|
||||||
|
_: 'users.getUsers',
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rawUsers.length !== id.length) {
|
||||||
|
// not saved (or invalid hash), not found by id
|
||||||
|
// find that user in chat participants list
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'messages.getFullChat',
|
||||||
|
chatId: update.chatId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = res.users.find(
|
||||||
|
(it) => it.id === update.fromId
|
||||||
|
)
|
||||||
|
if (!user) {
|
||||||
|
debug(
|
||||||
|
"received updateShortChatMessage, but wasn't able to find User"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rawUsers.push(user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const rawChats = await this.call({
|
const rawChats = await this.call({
|
||||||
_: 'messages.getChats',
|
_: 'messages.getChats',
|
||||||
|
@ -548,6 +715,11 @@ export function _handleUpdate(
|
||||||
chats: rawChats,
|
chats: rawChats,
|
||||||
})
|
})
|
||||||
this.dispatchUpdate(message, users, chats)
|
this.dispatchUpdate(message, users, chats)
|
||||||
|
} else if (update._ === 'updateShortSentMessage') {
|
||||||
|
// only store the new pts and date values
|
||||||
|
// we never need to dispatch this
|
||||||
|
this._date = update.date
|
||||||
|
this._pts = update.pts
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => this._emitError(err))
|
.catch((err) => this._emitError(err))
|
||||||
|
@ -560,5 +732,12 @@ export function _handleUpdate(
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function catchUp(this: TelegramClient): Promise<void> {
|
export function catchUp(this: TelegramClient): Promise<void> {
|
||||||
return _loadDifference.call(this)
|
// we also use a lock here so new updates are not processed
|
||||||
|
// while we are catching up with older ones
|
||||||
|
|
||||||
|
return this._updLock
|
||||||
|
.acquire()
|
||||||
|
.then(() => _loadDifference.call(this))
|
||||||
|
.catch((err) => this._emitError(err))
|
||||||
|
.then(() => this._updLock.release())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue