ForcePilot/backend/package/yuxi/channel/extensions/alexa/reminders.py
Kris 37493b6da6 feat(alexa): 新增Amazon Alexa渠道插件完整实现
新增了Alexa渠道的所有核心功能模块,包括配置管理、请求校验、会话管理、安全验证、去重、响应格式化、渐进式响应、主动推送、提醒功能、配对绑定以及Webhook端点,并完成了插件注册和配置项定义。
2026-05-21 10:38:31 +08:00

177 lines
5.2 KiB
Python

import logging
import time
from enum import Enum
import httpx
logger = logging.getLogger(__name__)
REMINDERS_PERMISSION = "alexa::alerts:reminders:skill:readwrite"
class RecurrenceFreq(str, Enum):
DAILY = "DAILY"
WEEKLY = "WEEKLY"
MONTHLY = "MONTHLY"
YEARLY = "YEARLY"
class RemindersService:
def __init__(self, account=None):
self._account = account
self._enabled = getattr(account, "reminders_enabled", False) if account else False
@property
def enabled(self) -> bool:
return self._enabled
@staticmethod
def check_permission(handler_input) -> bool:
try:
permissions = (
handler_input.request_envelope.context.system.user.permissions
)
if permissions and permissions.consent_token:
return True
except Exception:
pass
return False
async def create_absolute_reminder(
self,
api_access_token: str,
api_endpoint: str,
user_id: str,
message: str,
scheduled_time: str,
timezone_id: str = "Asia/Shanghai",
locale: str = "zh-CN",
) -> bool:
body = self._build_base_body(message, locale)
body["trigger"] = {
"type": "SCHEDULED_ABSOLUTE",
"scheduledTime": scheduled_time,
"timeZoneId": timezone_id,
}
return await self._send(api_access_token, api_endpoint, user_id, body, scheduled_time)
async def create_relative_reminder(
self,
api_access_token: str,
api_endpoint: str,
user_id: str,
message: str,
offset_seconds: int,
timezone_id: str = "Asia/Shanghai",
locale: str = "zh-CN",
) -> bool:
body = self._build_base_body(message, locale)
body["trigger"] = {
"type": "SCHEDULED_RELATIVE",
"offsetInSeconds": offset_seconds,
"timeZoneId": timezone_id,
}
return await self._send(
api_access_token, api_endpoint, user_id, body,
f"in {offset_seconds}s"
)
async def create_recurring_reminder(
self,
api_access_token: str,
api_endpoint: str,
user_id: str,
message: str,
scheduled_time: str,
frequency: RecurrenceFreq,
timezone_id: str = "Asia/Shanghai",
locale: str = "zh-CN",
) -> bool:
body = self._build_base_body(message, locale)
body["trigger"] = {
"type": "SCHEDULED_ABSOLUTE",
"scheduledTime": scheduled_time,
"timeZoneId": timezone_id,
"recurrence": {
"freq": frequency.value,
},
}
return await self._send(api_access_token, api_endpoint, user_id, body, scheduled_time)
async def create_reminder(
self,
api_access_token: str,
api_endpoint: str,
user_id: str,
message: str,
scheduled_time: str,
timezone_id: str = "Asia/Shanghai",
locale: str = "zh-CN",
) -> bool:
return await self.create_absolute_reminder(
api_access_token=api_access_token,
api_endpoint=api_endpoint,
user_id=user_id,
message=message,
scheduled_time=scheduled_time,
timezone_id=timezone_id,
locale=locale,
)
def _build_base_body(self, message: str, locale: str) -> dict:
return {
"requestTime": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"alertInfo": {
"spokenInfo": {
"content": [
{
"locale": locale,
"text": message,
}
]
}
},
"pushNotification": {
"status": "ENABLED",
},
}
async def _send(
self,
api_access_token: str,
api_endpoint: str,
user_id: str,
body: dict,
label: str,
) -> bool:
if not self._enabled:
logger.debug("Reminders disabled, skipping create")
return False
if not api_access_token or not api_endpoint:
logger.warning("Missing API credentials for reminder creation")
return False
url = f"{api_endpoint}/v1/alerts/reminders"
async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client:
try:
resp = await client.post(
url,
json=body,
headers={
"Authorization": f"Bearer {api_access_token}",
"Content-Type": "application/json",
},
)
if resp.status_code in (200, 201):
logger.info("Reminder created for user %s at %s", user_id, label)
return True
logger.warning(
"Reminder API returned %d: %s",
resp.status_code,
resp.text[:200],
)
except Exception:
logger.exception("Reminder creation failed")
return False