import os from yuxi.channel.extensions.alexa.types import AlexaAccount class AlexaConfig: ENV_MAP = { "application_id": "ALEXA_APPLICATION_ID", "skill_id": "ALEXA_SKILL_ID", "oauth_client_id": "ALEXA_OAUTH_CLIENT_ID", "oauth_client_secret": "ALEXA_OAUTH_CLIENT_SECRET", "oauth_introspect_url": "ALEXA_OAUTH_INTROSPECT_URL", "dm_policy": "ALEXA_DM_POLICY", "account_linking_enabled": "ALEXA_ACCOUNT_LINKING_ENABLED", "ssm_l_enabled": "ALEXA_SSML_ENABLED", "progressive_response_enabled": "ALEXA_PROGRESSIVE_RESPONSE", "proactive_events_enabled": "ALEXA_PROACTIVE_EVENTS_ENABLED", "reminders_enabled": "ALEXA_REMINDERS_ENABLED", "max_response_chars": "ALEXA_MAX_RESPONSE_CHARS", "session_persistence_enabled": "ALEXA_SESSION_PERSISTENCE", "session_timeout_seconds": "ALEXA_SESSION_TIMEOUT", } def list_account_ids(self, config: dict | None = None) -> list[str]: if self._resolve_application_id(): return ["default"] return [] def resolve_account(self, account_id: str = "default") -> AlexaAccount: return AlexaAccount( account_id=account_id, application_id=self._resolve_application_id() or "", skill_id=self._env_or_config("skill_id") or "", account_linking_enabled=self._env_or_config("account_linking_enabled") == "true", oauth_client_id=self._env_or_config("oauth_client_id") or "", oauth_client_secret=self._env_or_config("oauth_client_secret") or "", oauth_introspect_url=self._env_or_config("oauth_introspect_url") or "", dm_policy=self._env_or_config("dm_policy") or "open", ssm_l_enabled=self._env_or_config("ssm_l_enabled") != "false", progressive_response_enabled=self._env_or_config("progressive_response_enabled") == "true", progressive_response_timeout_ms=self._env_int("progressive_response_timeout_ms", 5500), proactive_events_enabled=self._env_or_config("proactive_events_enabled") == "true", reminders_enabled=self._env_or_config("reminders_enabled") == "true", max_response_chars=self._env_int("max_response_chars", 1000), session_persistence_enabled=self._env_or_config("session_persistence_enabled") == "true", session_timeout_seconds=self._env_int("session_timeout_seconds", 30), ) def is_configured(self, account: dict | None = None) -> bool: return bool(self._resolve_application_id()) def _resolve_application_id(self) -> str | None: return self._env_or_config("application_id") def _env_or_config(self, key: str) -> str | None: env_key = self.ENV_MAP.get(key, "") if env_key: val = os.getenv(env_key) if val: return val return None def _env_int(self, key: str, default: int) -> int: val = self._env_or_config(key) if val: try: return int(val) except ValueError: pass return default def config_schema(self) -> dict: return { "$schema": "https://json-schema.org/draft-07/schema#", "type": "object", "title": "Alexa 渠道配置", "properties": { "application_id": { "type": "string", "title": "Skill Application ID", "description": "Alexa Developer Console 中的 Skill Application ID (amzn1.ask.skill.*)", }, "skill_id": { "type": "string", "title": "Skill ID", "description": "用于 Skill ID 验证、Proactive Events/Reminders API 的 Skill ID", }, "account_linking_enabled": { "type": "boolean", "title": "启用 Account Linking", "default": False, "description": "是否强制要求 Alexa 用户通过 OAuth 2.0 绑定 ForcePilot 账号", }, "oauth_client_id": { "type": "string", "title": "OAuth Client ID", "description": "在 ForcePilot 后台创建的 OAuth Client ID", }, "oauth_client_secret": { "type": "string", "title": "OAuth Client Secret", "x-ui-password": True, "description": "OAuth Client Secret", }, "oauth_introspect_url": { "type": "string", "title": "OAuth Introspect URL", "description": "外部 OAuth Token 验证端点 URL,留空则使用 JWT 本地验证", }, "dm_policy": { "type": "string", "enum": ["open", "pairing", "allowlist", "disabled"], "default": "open", "title": "DM 策略", }, "progressive_response_enabled": { "type": "boolean", "title": "启用渐进式响应", "default": False, "description": "在 LLM 处理期间先发送'正在查询…'的中间语音", }, "proactive_events_enabled": { "type": "boolean", "title": "启用主动推送通知", "default": False, "description": "允许 ForcePilot 主动推送通知到 Alexa 设备", }, "reminders_enabled": { "type": "boolean", "title": "启用提醒功能", "default": False, "description": "允许为用户创建定时提醒", }, "max_response_chars": { "type": "integer", "title": "最大回复字符数", "default": 1000, "minimum": 100, "maximum": 8000, "description": "单次 SSML 语音响应最大字符数", }, "session_timeout_seconds": { "type": "integer", "title": "Session 超时时间(秒)", "default": 30, "minimum": 10, "maximum": 300, }, }, "required": ["application_id"], }