|
|
|
@@ -11,9 +11,14 @@
|
|
|
|
|
import ConfirmModal from '$lib/components/ConfirmModal.svelte';
|
|
|
|
|
import IconButton from '$lib/components/IconButton.svelte';
|
|
|
|
|
import { snackSuccess, snackError, snackInfo } from '$lib/stores/snackbar.svelte';
|
|
|
|
|
import type { TelegramChat } from '$lib/types';
|
|
|
|
|
import type { TelegramBot, TelegramChat } from '$lib/types';
|
|
|
|
|
|
|
|
|
|
let { settings, onreload }: { settings: any; onreload: () => Promise<void> } = $props();
|
|
|
|
|
interface CommandTrackerSummary { id: number; name: string; icon?: string; enabled: boolean }
|
|
|
|
|
interface ListenerEntry { listener_type: string; listener_id: number }
|
|
|
|
|
interface WebhookStatusInfo { url?: string; pending_update_count?: number; last_error_message?: string }
|
|
|
|
|
interface ApiResult { success: boolean; error?: string; verified?: boolean }
|
|
|
|
|
|
|
|
|
|
let { settings, onreload }: { settings: Record<string, string>; onreload: () => Promise<void> } = $props();
|
|
|
|
|
|
|
|
|
|
let bots = $derived(telegramBotsCache.items);
|
|
|
|
|
let showForm = $state(false);
|
|
|
|
@@ -21,7 +26,7 @@
|
|
|
|
|
let form = $state({ name: '', icon: '', token: '' });
|
|
|
|
|
let error = $state('');
|
|
|
|
|
let submitting = $state(false);
|
|
|
|
|
let confirmDelete = $state<any>(null);
|
|
|
|
|
let confirmDelete = $state<{ id: number; onconfirm: () => Promise<void> } | null>(null);
|
|
|
|
|
|
|
|
|
|
// Per-bot expandable sections
|
|
|
|
|
let chats = $state<Record<number, TelegramChat[]>>({});
|
|
|
|
@@ -29,17 +34,17 @@
|
|
|
|
|
let expandedSection = $state<Record<number, string>>({});
|
|
|
|
|
|
|
|
|
|
// Webhook status per bot
|
|
|
|
|
let webhookStatus = $state<Record<number, any>>({});
|
|
|
|
|
let webhookStatus = $state<Record<number, WebhookStatusInfo>>({});
|
|
|
|
|
|
|
|
|
|
let chatTesting = $state<Record<string, boolean>>({});
|
|
|
|
|
let modeChanging = $state<Record<number, boolean>>({});
|
|
|
|
|
|
|
|
|
|
// Listener status: command trackers using this bot
|
|
|
|
|
let botListenerStatus = $state<Record<number, any[]>>({});
|
|
|
|
|
let botListenerStatus = $state<Record<number, CommandTrackerSummary[]>>({});
|
|
|
|
|
let botListenerLoading = $state<Record<number, boolean>>({});
|
|
|
|
|
|
|
|
|
|
function openNew() { form = { name: '', icon: '', token: '' }; editing = null; showForm = true; }
|
|
|
|
|
function editBot(bot: any) { form = { name: bot.name, icon: bot.icon || '', token: '' }; editing = bot.id; showForm = true; }
|
|
|
|
|
function editBot(bot: TelegramBot) { form = { name: bot.name, icon: bot.icon || '', token: '' }; editing = bot.id; showForm = true; }
|
|
|
|
|
|
|
|
|
|
async function saveBot(e: SubmitEvent) {
|
|
|
|
|
e.preventDefault(); error = ''; submitting = true;
|
|
|
|
@@ -78,14 +83,14 @@
|
|
|
|
|
|
|
|
|
|
async function loadChats(botId: number) {
|
|
|
|
|
chatsLoading = { ...chatsLoading, [botId]: true };
|
|
|
|
|
try { chats = { ...chats, [botId]: await api(`/telegram-bots/${botId}/chats`) }; } catch { chats = { ...chats, [botId]: [] }; }
|
|
|
|
|
try { chats = { ...chats, [botId]: await api<TelegramChat[]>(`/telegram-bots/${botId}/chats`) }; } catch (e) { console.warn('Failed to load chats:', e); chats = { ...chats, [botId]: [] }; }
|
|
|
|
|
chatsLoading = { ...chatsLoading, [botId]: false };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function discoverChats(botId: number) {
|
|
|
|
|
chatsLoading = { ...chatsLoading, [botId]: true };
|
|
|
|
|
try {
|
|
|
|
|
chats = { ...chats, [botId]: await api(`/telegram-bots/${botId}/chats/discover`, { method: 'POST' }) };
|
|
|
|
|
chats = { ...chats, [botId]: await api<TelegramChat[]>(`/telegram-bots/${botId}/chats/discover`, { method: 'POST' }) };
|
|
|
|
|
snackSuccess(t('telegramBot.chatsDiscovered'));
|
|
|
|
|
} catch (err: any) { snackError(err.message); }
|
|
|
|
|
chatsLoading = { ...chatsLoading, [botId]: false };
|
|
|
|
@@ -94,7 +99,7 @@
|
|
|
|
|
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);
|
|
|
|
|
chats[botId] = (chats[botId] || []).filter((c) => c.id !== chatDbId);
|
|
|
|
|
snackSuccess(t('telegramBot.chatDeleted'));
|
|
|
|
|
} catch (err: any) { snackError(err.message); }
|
|
|
|
|
}
|
|
|
|
@@ -102,24 +107,24 @@
|
|
|
|
|
async function loadListenerStatus(botId: number) {
|
|
|
|
|
botListenerLoading = { ...botListenerLoading, [botId]: true };
|
|
|
|
|
try {
|
|
|
|
|
const trackers = await api('/command-trackers');
|
|
|
|
|
const matched: any[] = [];
|
|
|
|
|
const trackers = await api<CommandTrackerSummary[]>('/command-trackers');
|
|
|
|
|
const matched: CommandTrackerSummary[] = [];
|
|
|
|
|
for (const trk of trackers) {
|
|
|
|
|
try {
|
|
|
|
|
const listeners = await api(`/command-trackers/${trk.id}/listeners`);
|
|
|
|
|
const hasBot = listeners.some((l: any) => l.listener_type === 'telegram_bot' && l.listener_id === botId);
|
|
|
|
|
const listeners = await api<ListenerEntry[]>(`/command-trackers/${trk.id}/listeners`);
|
|
|
|
|
const hasBot = listeners.some((l) => l.listener_type === 'telegram_bot' && l.listener_id === botId);
|
|
|
|
|
if (hasBot) matched.push(trk);
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
} catch (e) { console.warn('Failed to load listeners for tracker:', e); }
|
|
|
|
|
}
|
|
|
|
|
botListenerStatus = { ...botListenerStatus, [botId]: matched };
|
|
|
|
|
} catch { botListenerStatus = { ...botListenerStatus, [botId]: [] }; }
|
|
|
|
|
} catch (e) { console.warn('Failed to load listener status:', e); botListenerStatus = { ...botListenerStatus, [botId]: [] }; }
|
|
|
|
|
botListenerLoading = { ...botListenerLoading, [botId]: false };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function syncCommands(botId: number) {
|
|
|
|
|
modeChanging = { ...modeChanging, [botId]: true };
|
|
|
|
|
try {
|
|
|
|
|
const res = await api(`/telegram-bots/${botId}/sync-commands`, { method: 'POST' });
|
|
|
|
|
const res = await api<ApiResult>(`/telegram-bots/${botId}/sync-commands`, { method: 'POST' });
|
|
|
|
|
if (res.success) snackSuccess(t('telegramBot.commandsSynced'));
|
|
|
|
|
else snackError(res.error || 'Failed');
|
|
|
|
|
} catch (err: any) { snackError(err.message); }
|
|
|
|
@@ -141,14 +146,14 @@
|
|
|
|
|
|
|
|
|
|
async function loadWebhookStatus(botId: number) {
|
|
|
|
|
try {
|
|
|
|
|
webhookStatus = { ...webhookStatus, [botId]: await api(`/telegram-bots/${botId}/webhook/status`) };
|
|
|
|
|
} catch { webhookStatus = { ...webhookStatus, [botId]: null }; }
|
|
|
|
|
webhookStatus = { ...webhookStatus, [botId]: await api<WebhookStatusInfo>(`/telegram-bots/${botId}/webhook/status`) };
|
|
|
|
|
} catch (e) { console.warn('Failed to load webhook status:', e); webhookStatus = { ...webhookStatus, [botId]: {} }; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function registerWebhook(botId: number) {
|
|
|
|
|
modeChanging = { ...modeChanging, [botId]: true };
|
|
|
|
|
try {
|
|
|
|
|
const res = await api(`/telegram-bots/${botId}/webhook/register`, { method: 'POST' });
|
|
|
|
|
const res = await api<ApiResult>(`/telegram-bots/${botId}/webhook/register`, { method: 'POST' });
|
|
|
|
|
if (res.success) {
|
|
|
|
|
snackSuccess(res.verified ? t('telegramBot.webhookVerified') : t('telegramBot.webhookRegistered'));
|
|
|
|
|
await loadWebhookStatus(botId);
|
|
|
|
@@ -162,7 +167,7 @@
|
|
|
|
|
async function unregisterWebhook(botId: number) {
|
|
|
|
|
modeChanging = { ...modeChanging, [botId]: true };
|
|
|
|
|
try {
|
|
|
|
|
const res = await api(`/telegram-bots/${botId}/webhook/unregister`, { method: 'POST' });
|
|
|
|
|
const res = await api<ApiResult>(`/telegram-bots/${botId}/webhook/unregister`, { method: 'POST' });
|
|
|
|
|
if (res.success) { snackSuccess(t('telegramBot.webhookUnregistered')); await loadWebhookStatus(botId); }
|
|
|
|
|
else snackError(res.error || 'Failed');
|
|
|
|
|
} catch (err: any) { snackError(err.message); }
|
|
|
|
@@ -193,7 +198,7 @@
|
|
|
|
|
if (chatTesting[key]) return;
|
|
|
|
|
chatTesting = { ...chatTesting, [key]: true };
|
|
|
|
|
try {
|
|
|
|
|
const res = await api(`/telegram-bots/${botId}/chats/${chatId}/test?locale=${getLocale()}`, { method: 'POST' });
|
|
|
|
|
const res = await api<ApiResult>(`/telegram-bots/${botId}/chats/${chatId}/test?locale=${getLocale()}`, { method: 'POST' });
|
|
|
|
|
if (res.success) snackSuccess(t('snack.targetTestSent'));
|
|
|
|
|
else snackError(res.error || 'Failed');
|
|
|
|
|
} catch (err: any) { snackError(err.message); }
|
|
|
|
@@ -398,7 +403,7 @@
|
|
|
|
|
{@const ws = webhookStatus[bot.id]}
|
|
|
|
|
<span class="text-xs font-mono {ws.url ? 'text-blue-500' : 'text-[var(--color-muted-foreground)]'}">
|
|
|
|
|
{ws.url ? t('telegramBot.webhookActive') : t('telegramBot.webhookNotSet')}
|
|
|
|
|
{#if ws.pending_update_count > 0}
|
|
|
|
|
{#if (ws.pending_update_count ?? 0) > 0}
|
|
|
|
|
({ws.pending_update_count} {t('telegramBot.pendingUpdates')})
|
|
|
|
|
{/if}
|
|
|
|
|
</span>
|
|
|
|
|