120 lines
5.0 KiB
Python
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}
|