新增了Alexa渠道的所有核心功能模块,包括配置管理、请求校验、会话管理、安全验证、去重、响应格式化、渐进式响应、主动推送、提醒功能、配对绑定以及Webhook端点,并完成了插件注册和配置项定义。
177 lines
5.2 KiB
Python
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 |