chore(tdlib): upgrade tdl 8.0.0 → 8.1.0 and prebuilt-tdlib 1.8.50 → 1.8.64

12 versions of TDLib bug fixes, performance improvements, and stricter
type definitions in @prebuilt-tdlib/types.

Two API breakages handled:

1. `getChatFolders` (plural) was removed — folder IDs now arrive via
   the `updateChatFolders` update event. Replaced the synchronous call
   with a 200ms event listener; if no folders arrive, we proceed with
   just main + archive lists. Chats inside folders are still reachable
   from chatListMain so this isn't a functional regression.

2. The new tdl `Client.invoke` signature requires a literal `_` field
   and rejects `Record<string, any>` shapes. Our `invokeWithTimeout`
   wrapper is intentionally generic — cast through `any` at the call
   site with a comment explaining why.

Both worker and bot type-check + build cleanly with the new versions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 08:45:44 +02:00
parent 2ccc9820cd
commit 18a0efb3d4
7 changed files with 200 additions and 92 deletions

View File

@@ -5,6 +5,36 @@ import { withFloodWait } from "../util/retry.js";
const log = childLogger("chats");
/**
* Collect chat folder IDs to widen the loadChats sweep across all folder
* chat lists. In TDLib 1.8.64+ there's no synchronous getChatFolders call —
* the folder list arrives via updateChatFolders. We listen for it briefly
* (200ms) and fall back to an empty list if nothing arrives; chats inside
* folders are still reachable via chatListMain so this only loses some
* preemptive cache warming.
*/
async function collectFolderIds(
client: Client
): Promise<{ _: "chatListFolder"; chat_folder_id: number }[]> {
return new Promise((resolve) => {
const ids: number[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handler = (update: any) => {
if (update?._ === "updateChatFolders") {
const folders = update.chat_folders as { id: number }[] | undefined;
if (folders) {
for (const f of folders) ids.push(f.id);
}
}
};
client.on("update", handler);
setTimeout(() => {
client.off("update", handler);
resolve(ids.map((id) => ({ _: "chatListFolder" as const, chat_folder_id: id })));
}, 200);
});
}
export interface TelegramChatInfo {
chatId: bigint;
title: string;
@@ -37,21 +67,16 @@ export async function getAccountChats(
// First, load all chats into TDLib's cache using loadChats (the proper API).
// loadChats returns 404 when all chats have been loaded.
// Then use getChats to retrieve the IDs for enrichment.
// Load from main, archive, AND chat folders to cover all chat types.
const folderLists: { _: "chatListFolder"; chat_folder_id: number }[] = [];
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const folders = (await client.invoke({ _: "getChatFolders" })) as any;
if (folders?.chat_folders) {
for (const f of folders.chat_folders) {
folderLists.push({ _: "chatListFolder", chat_folder_id: f.id });
}
}
} catch {
// getChatFolders may not be available in older TDLib versions
}
//
// Folder-specific loading (chatListFolder) was removed in TDLib 1.8.64+ —
// getChatFolders (plural) is no longer a callable method, only the
// updateChatFolders event. The chats inside folders are still reachable
// via chatListMain so this isn't a functional regression.
const folderLists: { _: "chatListFolder"; chat_folder_id: number }[] =
await collectFolderIds(client);
const chatLists: Record<string, unknown>[] = [
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const chatLists: any[] = [
{ _: "chatListMain" },
{ _: "chatListArchive" },
...folderLists,

View File

@@ -115,7 +115,10 @@ export async function invokeWithTimeout<T>(
}
}, timeoutMs);
(client.invoke(request) as Promise<T>)
// The tdl 8.1+ types are very strict about the literal `_` field;
// our generic wrapper passes arbitrary requests, so cast through any.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(client.invoke(request as any) as Promise<T>)
.then((result) => {
if (!settled) {
settled = true;

View File

@@ -357,24 +357,32 @@ export async function runWorkerForAccount(
// Load all chats into TDLib's local cache using loadChats (the recommended API).
// Without this, getChat/searchChatMessages fail with "Chat not found".
// loadChats returns a 404 when all chats have been loaded — that's the stop signal.
// Load from main, archive, AND chat folders to cover all chat types.
//
// TDLib 1.8.64+ removed the synchronous getChatFolders call; folder IDs
// now arrive only via the updateChatFolders event. We listen briefly,
// then load main + archive + any folders we caught. Chats inside folders
// are also reachable from chatListMain so missing the folder sweep is
// not a functional regression — it just loses a small bit of cache warming.
{
// Discover chat folders first
const folderLists: { _: "chatListFolder"; chat_folder_id: number }[] = [];
try {
const folders = await client.invoke({ _: "getChatFolders" }) as {
chat_folders?: { id: number }[];
};
if (folders.chat_folders) {
for (const f of folders.chat_folders) {
folderLists.push({ _: "chatListFolder", chat_folder_id: f.id });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const folderLists: any[] = await new Promise((resolve) => {
const ids: number[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handler = (update: any) => {
if (update?._ === "updateChatFolders") {
const folders = update.chat_folders as { id: number }[] | undefined;
if (folders) for (const f of folders) ids.push(f.id);
}
}
} catch {
// getChatFolders may not be available in older TDLib versions
}
};
client.on("update", handler);
setTimeout(() => {
client.off("update", handler);
resolve(ids.map((id) => ({ _: "chatListFolder", chat_folder_id: id })));
}, 200);
});
const chatLists: Record<string, unknown>[] = [
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const chatLists: any[] = [
{ _: "chatListMain" },
{ _: "chatListArchive" },
...folderLists,