feat: bridge_self bot commands — status, thresholds, reset, health
Adds bot commands for the bridge_self provider so operators can inspect and manage bridge health from chat: /status, /thresholds, /reset, /health. Includes Jinja2 templates for both locales, seed data, capability slots, and a handler that exposes pending deferred backlog plus per-counter reset. Also adds .claude/skills/ for project-scoped graph-aware skills.
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
import ReceiverSection from './ReceiverSection.svelte';
|
||||
import BotGroupHeader from './BotGroupHeader.svelte';
|
||||
|
||||
// в”Ђв”Ђ Helpers в”Ђв”Ђ
|
||||
// ──── Helpers ────
|
||||
|
||||
function getBotName(target: NotificationTarget): string | null {
|
||||
if (target.type === 'telegram' && target.config?.bot_id) {
|
||||
@@ -74,7 +74,7 @@
|
||||
return recv.receiver_key || '?';
|
||||
}
|
||||
|
||||
// в”Ђв”Ђ Constants в”Ђв”Ђ
|
||||
// ──── Constants ────
|
||||
|
||||
const ALL_TYPES = ['telegram', 'webhook', 'email', 'discord', 'slack', 'ntfy', 'matrix', 'broadcast'] as const;
|
||||
type TargetType = typeof ALL_TYPES[number];
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
function targetTiles(target: NotificationTarget): MetaTile[] {
|
||||
const tiles: MetaTile[] = [];
|
||||
// Type tile — useful when the "all types" filter is active and rows
|
||||
// Type tile — useful when the "all types" filter is active and rows
|
||||
// from multiple types appear side-by-side. The receivers count is
|
||||
// already shown inside the `target-summary` button, so we don't repeat
|
||||
// it as a tile.
|
||||
@@ -115,8 +115,8 @@
|
||||
tone: 'sky',
|
||||
});
|
||||
}
|
||||
// Telegram targets expose a chat label in config — surface it so the
|
||||
// row reads "Telegram Р’В· @bot Р’В· Family chat" without expanding.
|
||||
// Telegram targets expose a chat label in config — surface it so the
|
||||
// row reads "Telegram · @bot · Family chat" without expanding.
|
||||
const cfg = (target.config || {}) as Record<string, any>;
|
||||
if (target.type === 'telegram' && cfg.chat_id) {
|
||||
tiles.push({
|
||||
@@ -126,7 +126,7 @@
|
||||
mono: true,
|
||||
});
|
||||
}
|
||||
// Webhook target — show host
|
||||
// Webhook target — show host
|
||||
if (target.type === 'webhook' && cfg.url) {
|
||||
let host = String(cfg.url);
|
||||
try { host = new URL(host).host; } catch { /* keep raw */ }
|
||||
@@ -142,7 +142,7 @@
|
||||
return tiles;
|
||||
}
|
||||
|
||||
// в”Ђв”Ђ Derived state в”Ђв”Ђ
|
||||
// ──── Derived state ────
|
||||
|
||||
let allTargets = $derived(targetsCache.items);
|
||||
let activeType = $derived(page.url.searchParams.get('type') as TargetType | null);
|
||||
@@ -158,7 +158,7 @@
|
||||
const emailBotItems = $derived(emailBots.map(b => ({ value: b.id, label: b.name, icon: b.icon || 'mdiEmailOutline', desc: b.email })));
|
||||
const matrixBotItems = $derived(matrixBots.map(b => ({ value: b.id, label: b.name, icon: b.icon || 'mdiMatrix', desc: b.display_name || b.homeserver_url })));
|
||||
|
||||
// в”Ђв”Ђ Target form state в”Ђв”Ђ
|
||||
// ──── Target form state ────
|
||||
|
||||
let showForm = $state(false);
|
||||
let editing = $state<number | null>(null);
|
||||
@@ -204,7 +204,7 @@
|
||||
formEl?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
// в”Ђв”Ђ Receiver inline form state в”Ђв”Ђ
|
||||
// ──── Receiver inline form state ────
|
||||
|
||||
let addingReceiverForTarget = $state<number | null>(null);
|
||||
let receiverForm = $state<Record<string, any>>({});
|
||||
@@ -228,7 +228,7 @@
|
||||
if (!expandedTargets.has(id)) expandedTargets.add(id);
|
||||
}
|
||||
|
||||
// в”Ђв”Ђ Effects в”Ђв”Ђ
|
||||
// ──── Effects ────
|
||||
|
||||
// Reset form when switching target type tabs
|
||||
$effect(() => {
|
||||
@@ -239,11 +239,11 @@
|
||||
addingReceiverForTarget = null;
|
||||
});
|
||||
|
||||
// в”Ђв”Ђ Data loading в”Ђв”Ђ
|
||||
// ──── Data loading ────
|
||||
|
||||
onMount(load);
|
||||
|
||||
// в”Ђв”Ђ Bot grouping в”Ђв”Ђ
|
||||
// ──── Bot grouping ────
|
||||
|
||||
type TargetGroup = {
|
||||
key: string;
|
||||
@@ -372,7 +372,7 @@
|
||||
} catch (e) { console.warn('Failed to load bot chats:', e); }
|
||||
}
|
||||
|
||||
// Active discovery — actually polls Telegram getUpdates and persists any new chats.
|
||||
// Active discovery — actually polls Telegram getUpdates and persists any new chats.
|
||||
// Fired when the chat picker opens so the user sees the freshest list without a manual click.
|
||||
async function discoverReceiverBotChats(botId: number) {
|
||||
if (!botId) return;
|
||||
@@ -382,7 +382,7 @@
|
||||
} catch (e) { console.warn('Failed to discover bot chats:', e); }
|
||||
}
|
||||
|
||||
// в”Ђв”Ђ Target CRUD в”Ђв”Ђ
|
||||
// ──── Target CRUD ────
|
||||
|
||||
function openNew() {
|
||||
form = defaultForm();
|
||||
@@ -507,7 +507,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// в”Ђв”Ђ Receiver CRUD в”Ђв”Ђ
|
||||
// ──── Receiver CRUD ────
|
||||
|
||||
async function openReceiverForm(targetId: number, targetType: string) {
|
||||
// Force a remount of any picker palette when the same target is reopened
|
||||
|
||||
Reference in New Issue
Block a user