feat: add whatsapp web qr chat channel (#16238)

Adds a WhatsApp chat channel backed by a QR-based web login flow so users can connect without manual token setup.
This commit is contained in:
buua436
2026-06-23 17:45:31 +08:00
committed by GitHub
parent e15130534f
commit aba5d172bd
16 changed files with 3160 additions and 10 deletions

View File

@@ -33,7 +33,16 @@ import threading
LOGGER = logging.getLogger(__name__)
# Channel packages bundled under api/channels that self-register on import.
_BUNDLED_CHANNELS = ("feishu", "discord", "telegram", "line", "wecom", "qqbot", "dingtalk")
_BUNDLED_CHANNELS = (
"feishu",
"discord",
"telegram",
"line",
"wecom",
"qqbot",
"dingtalk",
"whatsapp",
)
# How often (seconds) to reconcile running channels against the database.
_RECONCILE_INTERVAL_SECS = 10
@@ -144,7 +153,10 @@ def _make_chat_handler(ch):
answer_text = ""
try:
async for ans in async_chat(dia, history, False, quote=False):
chat_kwargs = {"quote": False}
if "{knowledge}" in (dia.prompt_config or {}).get("system", ""):
chat_kwargs["knowledge"] = ""
async for ans in async_chat(dia, history, False, **chat_kwargs):
structure_answer(conv, ans, message_id, conv.id)
answer_text = (ans or {}).get("answer", "") or ""
ConversationService.update_by_id(conv.id, conv.to_dict())
@@ -228,6 +240,18 @@ async def _reconcile(running: dict, failed: dict) -> None:
if account_id not in desired or desired[account_id][2] != failed[account_id]:
failed.pop(account_id, None)
active_whatsapp = any(channel == "whatsapp" for channel, _, _ in desired.values())
if not active_whatsapp:
active_whatsapp = any(
entry["ch"].channel_id == "whatsapp" for entry in running.values()
)
from api.channels.whatsapp.gateway import sync_whatsapp_gateway
try:
await sync_whatsapp_gateway(active_whatsapp)
except Exception:
LOGGER.exception("failed to sync WhatsApp gateway enabled=%s", active_whatsapp)
# Start channels that are new (skip ones already known to fail with this config).
for account_id, (channel, credential, fp) in desired.items():
if account_id in running or failed.get(account_id) == fp: