feat: telegram bot (grammy webhook) — full parity channel
- pairing (admin code -> chat link), bundling of forwarded images via drafts+quiet-window sweeper - recipe pick via inline keyboard, job (origin=telegram) creation, one summary reply with result files + links - compressed-photo detection/flag; /start /help /rezepte /status - webhook route (secret token), admin pairing-code + links endpoints, AdminApp telegram section - worker notifies chat on completion; migration 002 adds jobs.telegram_chat_id; setupTelegram on init
This commit is contained in:
@@ -6,19 +6,31 @@ export default function AdminApp() {
|
||||
const [models, setModels] = useState<any[]>([]);
|
||||
const [avail, setAvail] = useState<any[]>([]);
|
||||
const [users, setUsers] = useState<any[]>([]);
|
||||
const [tgLinks, setTgLinks] = useState<any[]>([]);
|
||||
const [toast, setToast] = useState<string | null>(null);
|
||||
const [orKey, setOrKey] = useState(''); const [pdPw, setPdPw] = useState('');
|
||||
const notify = (t: string) => { setToast(t); setTimeout(() => setToast(null), 2600); };
|
||||
|
||||
const load = useCallback(async () => {
|
||||
const [a, b, c, d] = await Promise.all([
|
||||
const [a, b, c, d, e] = await Promise.all([
|
||||
fetch('/api/admin/settings').then((r) => r.json()),
|
||||
fetch('/api/admin/stats').then((r) => r.json()),
|
||||
fetch('/api/admin/models?available=1').then((r) => r.json()),
|
||||
fetch('/api/admin/users').then((r) => r.json()),
|
||||
fetch('/api/admin/telegram/links').then((r) => r.json()).catch(() => ({ links: [] })),
|
||||
]);
|
||||
setS(a.settings || {}); setStats(b); setModels(c.models || []); setAvail(c.available || []); setUsers(d.users || []);
|
||||
setS(a.settings || {}); setStats(b); setModels(c.models || []); setAvail(c.available || []);
|
||||
setUsers(d.users || []); setTgLinks(e.links || []);
|
||||
}, []);
|
||||
|
||||
const pairCode = async (userId: string) => {
|
||||
const r = await fetch('/api/admin/telegram/pairing-code', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId }) }).then((x) => x.json());
|
||||
if (r.code) alert(`Kopplungscode: ${r.code}\n\n15 Minuten gültig. Im Klarbild-Telegram-Chat eingeben.`);
|
||||
};
|
||||
const unlink = async (chat_id: number) => {
|
||||
await fetch('/api/admin/telegram/links', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id }) });
|
||||
load();
|
||||
};
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
const field = (k: string, v: any) => setS((p: any) => ({ ...p, [k]: v }));
|
||||
@@ -147,12 +159,29 @@ export default function AdminApp() {
|
||||
{users.map((u) => (
|
||||
<div key={u.id} className="zeile">
|
||||
<div><b>{u.display_name || u.username}</b> <span className="fein">{u.username} · {u.role}</span></div>
|
||||
<button className="mini" onClick={() => setPw(u.id)}>Passwort setzen</button>
|
||||
<div className="reihe">
|
||||
<button className="mini" onClick={() => pairCode(u.id)}>Telegram koppeln</button>
|
||||
<button className="mini" onClick={() => setPw(u.id)}>Passwort setzen</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="karte">
|
||||
<div className="kopfzeile"><span className="mono-label">Telegram-Bot</span></div>
|
||||
<div className="liste">
|
||||
<div className="fein">Über „Telegram koppeln" bei einem Nutzer einen Code erzeugen und im Chat mit dem Bot eingeben. Danach dort Bilder weiterleiten → Rezept wählen → Ergebnisse kommen zurück.</div>
|
||||
{tgLinks.map((l) => (
|
||||
<div key={l.chat_id} className="zeile">
|
||||
<div><b>Chat {l.chat_id}</b> <span className="fein">{l.user_name || ''} · seit {new Date(l.linked_at).toLocaleDateString('de-DE')}</span></div>
|
||||
<button className="mini" onClick={() => unlink(l.chat_id)}>Trennen</button>
|
||||
</div>
|
||||
))}
|
||||
{tgLinks.length === 0 && <div className="fein">Noch keine gekoppelten Chats.</div>}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{toast && <div className="toast">{toast}</div>}
|
||||
<AdminStyles />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user