Persist Telegram chats in DB, auto-save from webhooks, click-to-copy
All checks were successful
Validate / Hassfest (push) Successful in 4s
All checks were successful
Validate / Hassfest (push) Successful in 4s
- Add TelegramChat model (bot_id, chat_id, title, type, username) - Chats auto-saved when bot receives webhook messages - New API: GET/DELETE chats, POST discover (merges from getUpdates) - Cascade delete chats when bot is deleted - Frontend: click chat row to copy chat ID to clipboard - Frontend: delete button per chat, "Discover chats" sync button - Add 4 i18n keys (EN/RU) for chat management Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -194,7 +194,11 @@
|
||||
"rateSearch": "Search cooldown",
|
||||
"rateFind": "Find cooldown",
|
||||
"rateDefault": "Default cooldown",
|
||||
"syncCommands": "Sync to Telegram"
|
||||
"syncCommands": "Sync to Telegram",
|
||||
"discoverChats": "Discover chats from Telegram",
|
||||
"clickToCopy": "Click to copy chat ID",
|
||||
"chatsDiscovered": "Chats discovered",
|
||||
"chatDeleted": "Chat removed"
|
||||
},
|
||||
"trackingConfig": {
|
||||
"title": "Tracking Configs",
|
||||
|
||||
@@ -194,7 +194,11 @@
|
||||
"rateSearch": "Кулдаун поиска",
|
||||
"rateFind": "Кулдаун поиска файлов",
|
||||
"rateDefault": "Кулдаун по умолчанию",
|
||||
"syncCommands": "Синхронизировать с Telegram"
|
||||
"syncCommands": "Синхронизировать с Telegram",
|
||||
"discoverChats": "Обнаружить чаты из Telegram",
|
||||
"clickToCopy": "Нажмите, чтобы скопировать ID чата",
|
||||
"chatsDiscovered": "Чаты обнаружены",
|
||||
"chatDeleted": "Чат удалён"
|
||||
},
|
||||
"trackingConfig": {
|
||||
"title": "Конфигурации отслеживания",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import ConfirmModal from '$lib/components/ConfirmModal.svelte';
|
||||
import IconButton from '$lib/components/IconButton.svelte';
|
||||
import Hint from '$lib/components/Hint.svelte';
|
||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||
import { snackSuccess, snackError, snackInfo } from '$lib/stores/snackbar.svelte';
|
||||
|
||||
const ALL_COMMANDS = [
|
||||
'status', 'albums', 'events', 'summary', 'latest',
|
||||
@@ -82,11 +82,7 @@
|
||||
expandedSection[botId] = section;
|
||||
|
||||
if (section === 'chats') {
|
||||
chatsLoading[botId] = true;
|
||||
api(`/telegram-bots/${botId}/chats`)
|
||||
.then((data: any) => chats[botId] = data)
|
||||
.catch(() => chats[botId] = [])
|
||||
.finally(() => chatsLoading[botId] = false);
|
||||
loadChats(botId);
|
||||
}
|
||||
|
||||
if (section === 'commands') {
|
||||
@@ -95,6 +91,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadChats(botId: number) {
|
||||
chatsLoading[botId] = true;
|
||||
try { chats[botId] = await api(`/telegram-bots/${botId}/chats`); }
|
||||
catch { chats[botId] = []; }
|
||||
chatsLoading[botId] = false;
|
||||
}
|
||||
|
||||
async function discoverChats(botId: number) {
|
||||
chatsLoading[botId] = true;
|
||||
try {
|
||||
chats[botId] = await api(`/telegram-bots/${botId}/chats/discover`, { method: 'POST' });
|
||||
snackSuccess(t('telegramBot.chatsDiscovered'));
|
||||
} catch (err: any) { snackError(err.message); }
|
||||
chatsLoading[botId] = false;
|
||||
}
|
||||
|
||||
async function deleteChat(botId: number, chatDbId: number) {
|
||||
try {
|
||||
await api(`/telegram-bots/${botId}/chats/${chatDbId}`, { method: 'DELETE' });
|
||||
chats[botId] = (chats[botId] || []).filter((c: any) => c.id !== chatDbId);
|
||||
snackSuccess(t('telegramBot.chatDeleted'));
|
||||
} catch (err: any) { snackError(err.message); }
|
||||
}
|
||||
|
||||
function copyChatId(chatId: string) {
|
||||
navigator.clipboard.writeText(chatId);
|
||||
snackInfo(t('snack.copied') + ': ' + chatId);
|
||||
}
|
||||
|
||||
function toggleCommand(botId: number, cmd: string) {
|
||||
const cfg = editingConfig[botId];
|
||||
if (!cfg) return;
|
||||
@@ -211,19 +236,24 @@
|
||||
{:else}
|
||||
<div class="space-y-1">
|
||||
{#each chats[bot.id] as chat}
|
||||
<div class="flex items-center justify-between text-sm px-2 py-1 rounded hover:bg-[var(--color-muted)]">
|
||||
<div>
|
||||
<div class="flex items-center justify-between text-sm px-2 py-1 rounded hover:bg-[var(--color-muted)] group">
|
||||
<button class="flex items-center gap-2 text-left cursor-pointer"
|
||||
onclick={() => copyChatId(chat.chat_id)}
|
||||
title={t('telegramBot.clickToCopy')}>
|
||||
<span class="font-medium">{chat.title || chat.username || 'Unknown'}</span>
|
||||
<span class="text-xs ml-2 px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{chatTypeLabel(chat.type)}</span>
|
||||
</div>
|
||||
<span class="text-xs text-[var(--color-muted-foreground)] font-mono">{chat.id}</span>
|
||||
<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{chatTypeLabel(chat.type)}</span>
|
||||
<span class="text-xs text-[var(--color-muted-foreground)] font-mono">{chat.chat_id}</span>
|
||||
</button>
|
||||
<IconButton icon="mdiDelete" title={t('common.delete')} size={14}
|
||||
onclick={() => deleteChat(bot.id, chat.id)} variant="danger" />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<button onclick={() => toggleSection(bot.id, 'chats')}
|
||||
class="text-xs text-[var(--color-muted-foreground)] hover:underline mt-2">
|
||||
{t('telegramBot.refreshChats')}
|
||||
<button onclick={() => discoverChats(bot.id)}
|
||||
class="text-xs text-[var(--color-primary)] hover:underline mt-2 flex items-center gap-1">
|
||||
<MdiIcon name="mdiSync" size={14} />
|
||||
{t('telegramBot.discoverChats')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user