mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-05-10 22:01:16 +00:00
feat: support all chat types in channel discovery and enrich bot messages
Channel Discovery: - Remove channel/supergroup filter from getAccountChats — all chat types (private, groups, Saved Messages, etc.) are now discoverable as sources - Detect and label the self-chat as "Saved Messages" via getMe - Update channel picker dialog to accept any chat type string Bot Rich Messages: - Enhance package send preview with creator, file count, tags, and source channel info in MarkdownV2 caption - Include tags in new_package subscription notifications - Expand getPendingSendRequest to fetch richer package data Performance: - Reviewed pipeline for many-channel load — getChats pagination fix and per-channel getChat pre-load from prior commit address the main concerns - Channels with no new messages skip in 2-3 API calls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,9 +115,15 @@ export async function getPendingSendRequest(requestId: string) {
|
|||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
fileName: true,
|
fileName: true,
|
||||||
|
fileSize: true,
|
||||||
|
fileCount: true,
|
||||||
|
creator: true,
|
||||||
|
tags: true,
|
||||||
|
archiveType: true,
|
||||||
destChannelId: true,
|
destChannelId: true,
|
||||||
destMessageId: true,
|
destMessageId: true,
|
||||||
previewData: true,
|
previewData: true,
|
||||||
|
sourceChannel: { select: { title: true, telegramId: true } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
telegramLink: true,
|
telegramLink: true,
|
||||||
|
|||||||
@@ -134,9 +134,22 @@ async function processSendRequest(requestId: string): Promise<void> {
|
|||||||
throw new Error("No global destination channel configured");
|
throw new Error("No global destination channel configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send preview if available
|
// Send preview with rich caption if available
|
||||||
if (pkg.previewData) {
|
if (pkg.previewData) {
|
||||||
const caption = `📦 *${pkg.fileName}*\n\nSent from Dragon's Stash`;
|
const lines: string[] = [];
|
||||||
|
lines.push(`📦 *${escapeMarkdown(pkg.fileName)}*`);
|
||||||
|
if (pkg.creator) lines.push(`👤 ${escapeMarkdown(pkg.creator)}`);
|
||||||
|
if (pkg.fileCount > 0) lines.push(`📁 ${pkg.fileCount} files`);
|
||||||
|
if (pkg.tags && pkg.tags.length > 0) {
|
||||||
|
lines.push(`🏷️ ${pkg.tags.map((t: string) => escapeMarkdown(t)).join(", ")}`);
|
||||||
|
}
|
||||||
|
if (pkg.sourceChannel) {
|
||||||
|
lines.push(`📡 Source: ${escapeMarkdown(pkg.sourceChannel.title)}`);
|
||||||
|
}
|
||||||
|
lines.push("");
|
||||||
|
lines.push("_Sent from Dragon's Stash_");
|
||||||
|
|
||||||
|
const caption = lines.join("\n");
|
||||||
await sendPhotoMessage(targetUserId, Buffer.from(pkg.previewData), caption);
|
await sendPhotoMessage(targetUserId, Buffer.from(pkg.previewData), caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +202,9 @@ async function handleNewPackage(payload: string): Promise<void> {
|
|||||||
`🔔 <b>New package matching your subscriptions:</b>`,
|
`🔔 <b>New package matching your subscriptions:</b>`,
|
||||||
``,
|
``,
|
||||||
`📦 <b>${escapeHtml(data.fileName)}</b>${creator}`,
|
`📦 <b>${escapeHtml(data.fileName)}</b>${creator}`,
|
||||||
|
...(data.tags && data.tags.length > 0
|
||||||
|
? [`🏷️ ${data.tags.map((t: string) => escapeHtml(t)).join(", ")}`]
|
||||||
|
: []),
|
||||||
``,
|
``,
|
||||||
`Matched: ${patterns.map((p) => `"${escapeHtml(p)}"`).join(", ")}`,
|
`Matched: ${patterns.map((p) => `"${escapeHtml(p)}"`).join(", ")}`,
|
||||||
``,
|
``,
|
||||||
@@ -213,3 +229,7 @@ async function handleNewPackage(payload: string): Promise<void> {
|
|||||||
function escapeHtml(text: string): string {
|
function escapeHtml(text: string): string {
|
||||||
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeMarkdown(text: string): string {
|
||||||
|
return text.replace(/([_*[\]()~`>#+\-=|{}.!\\])/g, "\\$1");
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { ScrollArea } from "@/components/ui/scroll-area";
|
|||||||
interface FetchedChannel {
|
interface FetchedChannel {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: "channel" | "supergroup";
|
type: string;
|
||||||
isForum: boolean;
|
isForum: boolean;
|
||||||
memberCount: number | null;
|
memberCount: number | null;
|
||||||
alreadyLinked: boolean;
|
alreadyLinked: boolean;
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ export async function createPackageWithFiles(input: CreatePackageInput) {
|
|||||||
packageId: pkg.id,
|
packageId: pkg.id,
|
||||||
fileName: input.fileName,
|
fileName: input.fileName,
|
||||||
creator: input.creator ?? null,
|
creator: input.creator ?? null,
|
||||||
|
tags: input.tags ?? [],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -16,13 +16,24 @@ export interface TelegramChatInfo {
|
|||||||
/**
|
/**
|
||||||
* Fetch all chats the account is a member of.
|
* Fetch all chats the account is a member of.
|
||||||
* Uses TDLib's getChats to load the chat list, then getChat for details.
|
* Uses TDLib's getChats to load the chat list, then getChat for details.
|
||||||
* Filters to channels and supergroups only (groups/privates are not useful for ingestion).
|
* Returns ALL chat types: channels, supergroups, groups, private chats,
|
||||||
|
* and the special "Saved Messages" (self) chat.
|
||||||
*/
|
*/
|
||||||
export async function getAccountChats(
|
export async function getAccountChats(
|
||||||
client: Client
|
client: Client
|
||||||
): Promise<TelegramChatInfo[]> {
|
): Promise<TelegramChatInfo[]> {
|
||||||
const chats: TelegramChatInfo[] = [];
|
const chats: TelegramChatInfo[] = [];
|
||||||
|
|
||||||
|
// Get the current user's ID so we can label Saved Messages
|
||||||
|
let selfUserId: number | null = null;
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const me = (await client.invoke({ _: "getMe" })) as any;
|
||||||
|
selfUserId = me.id;
|
||||||
|
} catch {
|
||||||
|
log.warn("Failed to get current user via getMe");
|
||||||
|
}
|
||||||
|
|
||||||
// Load ALL chats from the main list by paginating getChats.
|
// Load ALL chats from the main list by paginating getChats.
|
||||||
// TDLib's getChats returns batches — keep calling until it returns
|
// TDLib's getChats returns batches — keep calling until it returns
|
||||||
// an empty list, which signals all chats have been loaded.
|
// an empty list, which signals all chats have been loaded.
|
||||||
@@ -56,6 +67,7 @@ export async function getAccountChats(
|
|||||||
const chatType = chat.type?._;
|
const chatType = chat.type?._;
|
||||||
let type: TelegramChatInfo["type"] = "other";
|
let type: TelegramChatInfo["type"] = "other";
|
||||||
let isForum = false;
|
let isForum = false;
|
||||||
|
let title = chat.title ?? `Chat ${chatId}`;
|
||||||
|
|
||||||
if (chatType === "chatTypeSupergroup") {
|
if (chatType === "chatTypeSupergroup") {
|
||||||
// Get supergroup details to check if it's a channel or group
|
// Get supergroup details to check if it's a channel or group
|
||||||
@@ -78,17 +90,18 @@ export async function getAccountChats(
|
|||||||
type = "group";
|
type = "group";
|
||||||
} else if (chatType === "chatTypePrivate" || chatType === "chatTypeSecret") {
|
} else if (chatType === "chatTypePrivate" || chatType === "chatTypeSecret") {
|
||||||
type = "private";
|
type = "private";
|
||||||
|
// Label the self-chat as "Saved Messages"
|
||||||
|
if (selfUserId !== null && chat.type?.user_id === selfUserId) {
|
||||||
|
title = "Saved Messages";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only include channels and supergroups
|
chats.push({
|
||||||
if (type === "channel" || type === "supergroup") {
|
chatId: BigInt(chatId),
|
||||||
chats.push({
|
title,
|
||||||
chatId: BigInt(chatId),
|
type,
|
||||||
title: chat.title ?? `Chat ${chatId}`,
|
isForum,
|
||||||
type,
|
});
|
||||||
isForum,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.warn({ chatId, err }, "Failed to get chat details, skipping");
|
log.warn({ chatId, err }, "Failed to get chat details, skipping");
|
||||||
}
|
}
|
||||||
@@ -99,7 +112,7 @@ export async function getAccountChats(
|
|||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
{ total: chats.length },
|
{ total: chats.length },
|
||||||
"Fetched channels/supergroups from Telegram"
|
"Fetched all chats from Telegram"
|
||||||
);
|
);
|
||||||
|
|
||||||
return chats;
|
return chats;
|
||||||
|
|||||||
Reference in New Issue
Block a user