Files
beaver_project/external-connector/external_connector/providers/fake.py

120 lines
5.0 KiB
Python

from __future__ import annotations
import base64
from typing import Any
from uuid import uuid4
from external_connector.state import ConnectorSessionState, SidecarStateStore
def _session_view(session: ConnectorSessionState) -> dict[str, Any]:
return {
"sessionId": session.session_id,
"kind": session.kind,
"status": session.status,
"qrCode": session.qr_code,
"qrImage": session.qr_image,
"instructions": list(session.instructions),
"accountId": session.account_id,
"displayName": session.display_name if session.account_id else None,
"error": session.error,
"metadata": dict(session.metadata),
}
def _fake_qr_image() -> str:
svg = (
'<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">'
'<rect width="256" height="256" fill="#fff"/>'
'<rect x="16" y="16" width="58" height="58" fill="#111"/>'
'<rect x="28" y="28" width="34" height="34" fill="#fff"/>'
'<rect x="184" y="16" width="58" height="58" fill="#111"/>'
'<rect x="196" y="28" width="34" height="34" fill="#fff"/>'
'<rect x="16" y="184" width="58" height="58" fill="#111"/>'
'<rect x="28" y="196" width="34" height="34" fill="#fff"/>'
'<rect x="96" y="96" width="18" height="18" fill="#111"/>'
'<rect x="132" y="96" width="18" height="18" fill="#111"/>'
'<rect x="168" y="96" width="18" height="18" fill="#111"/>'
'<rect x="96" y="132" width="18" height="18" fill="#111"/>'
'<rect x="150" y="132" width="18" height="18" fill="#111"/>'
'<rect x="204" y="132" width="18" height="18" fill="#111"/>'
'<rect x="114" y="168" width="18" height="18" fill="#111"/>'
'<rect x="168" y="168" width="18" height="18" fill="#111"/>'
'<rect x="204" y="168" width="18" height="18" fill="#111"/>'
'<rect x="96" y="204" width="18" height="18" fill="#111"/>'
'<rect x="132" y="204" width="18" height="18" fill="#111"/>'
'<rect x="186" y="204" width="18" height="18" fill="#111"/>'
'<text x="128" y="248" text-anchor="middle" font-family="monospace" font-size="14" fill="#444">FAKE QR</text>'
"</svg>"
)
encoded = base64.b64encode(svg.encode("utf-8")).decode("ascii")
return f"data:image/svg+xml;base64,{encoded}"
class FakeProvider:
provider_id = "fake"
def __init__(self, store: SidecarStateStore) -> None:
self.store = store
def connectors(self) -> list[dict[str, Any]]:
return [
{
"kind": "weixin",
"displayName": "Weixin",
"authType": "qr",
"providerId": self.provider_id,
"capabilities": ["receive_text", "send_text", "receive_media", "direct_messages"],
},
{
"kind": "feishu",
"displayName": "Feishu/Lark",
"authType": "plugin_install",
"providerId": self.provider_id,
"capabilities": ["receive_text", "send_text", "receive_media", "groups"],
},
]
def health(self) -> dict[str, Any]:
return {"ok": True, "providerId": self.provider_id}
def start_session(self, payload: dict[str, Any]) -> dict[str, Any]:
session = self.store.create_session(
kind=str(payload["kind"]),
connection_id=str(payload["connectionId"]),
channel_id=str(payload["channelId"]),
display_name=str(payload["displayName"]),
options=dict(payload.get("options") or {}),
)
session = self.store.update_session(
session.session_id,
status="qr_ready" if session.kind == "weixin" else "waiting_for_user",
qr_image=_fake_qr_image() if session.kind == "weixin" else None,
instructions=["Run the provider install flow and finish verification"] if session.kind == "feishu" else [],
)
return _session_view(session)
def get_session(self, session_id: str) -> dict[str, Any]:
return _session_view(self.store.get_session(session_id))
def cancel_session(self, session_id: str) -> None:
self.store.update_session(session_id, status="cancelled")
def logout(self, connection_id: str) -> None:
return None
def send(self, payload: dict[str, Any]) -> dict[str, Any]:
begin = self.store.begin_send(connection_id=str(payload["connectionId"]), request_id=str(payload["requestId"]))
if not begin.should_send:
if begin.http_status == 409:
return {
"ok": False,
"status": begin.status,
"retryAfterSeconds": begin.retry_after_seconds,
"httpStatus": 409,
}
return {"ok": True, "providerMessageId": begin.provider_message_id}
provider_message_id = f"fake_{uuid4().hex}"
self.store.complete_send(begin.dedupe_key, provider_message_id=provider_message_id)
return {"ok": True, "providerMessageId": provider_message_id}