优化定时订阅内容
This commit is contained in:
@@ -13,6 +13,7 @@ from utils.decorator.plugin_decorators import plugin_stats_decorator
|
||||
from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager
|
||||
from utils.decorator.points_decorator import plugin_points_cost
|
||||
from wechat_ipad import WechatAPIClient
|
||||
from utils.decorator.async_job import async_job
|
||||
|
||||
|
||||
# ================= Redis 管理器 =================
|
||||
@@ -113,8 +114,9 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
self.feature = self.register_feature()
|
||||
self.redis_manager = None
|
||||
self._config = {}
|
||||
self.bot_client = None
|
||||
self.task = None
|
||||
# 使用统一的异步定时任务系统,避免手写休眠循环
|
||||
async_job.at_times(["08:00"])(self._execute_daily_push)
|
||||
self.bot: WechatAPIClient = None
|
||||
|
||||
def initialize(self, context: Dict[str, Any]) -> bool:
|
||||
"""初始化"""
|
||||
@@ -136,14 +138,11 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
def start(self) -> bool:
|
||||
self.LOG.info(f"[{self.name}] 插件已启动")
|
||||
self.status = PluginStatus.RUNNING
|
||||
self.task = asyncio.create_task(self._daily_scheduler())
|
||||
return True
|
||||
|
||||
def stop(self) -> bool:
|
||||
self.LOG.info(f"[{self.name}] 插件已停止")
|
||||
self.status = PluginStatus.STOPPED
|
||||
if self.task:
|
||||
self.task.cancel()
|
||||
return True
|
||||
|
||||
def can_process(self, message: Dict[str, Any]) -> bool:
|
||||
@@ -160,9 +159,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
sender = message.get("sender")
|
||||
roomid = message.get("roomid", "")
|
||||
gbm: GroupBotManager = message.get("gbm")
|
||||
bot: WechatAPIClient = message.get("bot")
|
||||
|
||||
if not self.bot_client: self.bot_client = bot
|
||||
self.bot: WechatAPIClient = message.get("bot")
|
||||
|
||||
if roomid and gbm.get_group_permission(roomid, self.feature) == PermissionStatus.DISABLED:
|
||||
return False, "没有权限"
|
||||
@@ -202,7 +199,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
|
||||
# 反馈时告诉用户标准名
|
||||
msg = f"✅ 订阅成功!\n已锁定城市:{city_info['adm1']} - {std_city_name}\n每天 08:00 推送早报。"
|
||||
await bot.send_text_message(roomid or sender, msg, sender)
|
||||
await self.bot.send_text_message(roomid or sender, msg, sender)
|
||||
|
||||
# Step C: 顺便存历史 (用于明天对比)
|
||||
# 使用 ID 获取天气,避免再次 Geo 查询
|
||||
@@ -217,7 +214,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
if not self.redis_manager: return False, "数据库不可用"
|
||||
unique_id = f"{roomid}_{sender}" if roomid else f"private_{sender}"
|
||||
self.redis_manager.remove_subscription(unique_id)
|
||||
await bot.send_text_message(roomid or sender, "✅ 已取消天气订阅。", sender)
|
||||
await self.bot.send_text_message(roomid or sender, "✅ 已取消天气订阅。", sender)
|
||||
return True, "取消成功"
|
||||
|
||||
# 3. 普通查询 (保持原逻辑,但也建议先 lookup 再查,或者复用 lookup)
|
||||
@@ -230,35 +227,12 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
weather_text = await self._get_weather_text_response(input_city)
|
||||
if not weather_text: return False, "查询失败"
|
||||
|
||||
await bot.send_text_message(roomid or sender, weather_text, sender)
|
||||
await self.bot.send_text_message(roomid or sender, weather_text, sender)
|
||||
return True, "发送成功"
|
||||
|
||||
# ================= 定时任务系统 =================
|
||||
|
||||
async def _daily_scheduler(self):
|
||||
"""每日 08:00 调度器"""
|
||||
self.LOG.info("⏰ 天气订阅定时任务已启动...")
|
||||
while self.status == PluginStatus.RUNNING:
|
||||
try:
|
||||
now = datetime.datetime.now()
|
||||
target_time = now.replace(hour=8, minute=0, second=0, microsecond=0)
|
||||
if now > target_time:
|
||||
target_time += datetime.timedelta(days=1)
|
||||
|
||||
wait_seconds = (target_time - now).total_seconds()
|
||||
self.LOG.info(f"⏳ 下一次天气推送将在 {wait_seconds:.0f} 秒后")
|
||||
|
||||
await asyncio.sleep(wait_seconds)
|
||||
|
||||
if self.bot_client and self.redis_manager:
|
||||
await self._execute_daily_push()
|
||||
|
||||
await asyncio.sleep(60)
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as e:
|
||||
self.LOG.error(f"定时任务异常: {e}")
|
||||
await asyncio.sleep(60)
|
||||
|
||||
|
||||
async def _execute_daily_push(self):
|
||||
"""执行全量推送 (基于 ID 聚合)"""
|
||||
@@ -305,9 +279,9 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
try:
|
||||
if self.bot_client:
|
||||
await self.bot_client.send_text_message(target_id, final_msg,
|
||||
sender_id if room_id else None)
|
||||
if self.bot:
|
||||
await self.bot.send_at_message(target_id, final_msg,
|
||||
[sender_id])
|
||||
except Exception as send_e:
|
||||
self.LOG.error(f"推送给 {target_id} 失败: {send_e}")
|
||||
|
||||
@@ -340,7 +314,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
if life_tips: msg += "\n💡 **温馨提示**\n" + "\n".join(life_tips) + "\n"
|
||||
|
||||
msg += f"\n👔 **穿衣**:{clothing}"
|
||||
|
||||
|
||||
# 增加一些趣味数据
|
||||
msg += self._get_fun_stats(today)
|
||||
|
||||
@@ -378,7 +352,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
alerts.append("❄️ **冰雪天气**:路面湿滑,注意行车安全。")
|
||||
elif "冰雹" in text_day:
|
||||
alerts.append("☄️ **冰雹警报**:赶紧把车停到室内!")
|
||||
|
||||
|
||||
# 4. 风力
|
||||
try:
|
||||
# windScaleDay 可能是 "1-2" 或 "3"
|
||||
@@ -390,12 +364,12 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
alerts.append(f"🌬️ **大风警报**:{wind_dir}{wind_scale}级,发型要乱啦。")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
return alerts
|
||||
|
||||
def _generate_life_tips(self, data: dict) -> List[str]:
|
||||
tips = []
|
||||
|
||||
|
||||
# 1. 降水 (precip)
|
||||
precip = float(data.get('precip', 0.0))
|
||||
text_day = data.get('textDay', '')
|
||||
@@ -415,14 +389,14 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
tips.append("☠️ **紫外线爆表**:尽量留在室内,出门必须全副武装!")
|
||||
elif uv >= 6:
|
||||
tips.append("☂️ **紫外线强**:涂好防晒霜,戴上墨镜。")
|
||||
|
||||
|
||||
# 3. 湿度 (humidity)
|
||||
hum = int(data.get('humidity', 0))
|
||||
if hum >= 90:
|
||||
tips.append("💧 空气像是能拧出水(湿度>90%),注意防潮除湿。")
|
||||
elif hum <= 20:
|
||||
tips.append("🌵 空气很干燥(湿度<20%),多喝水,小心静电。")
|
||||
|
||||
|
||||
# 4. 气压 (pressure)
|
||||
pres = int(data.get('pressure', 1000))
|
||||
if pres < 995:
|
||||
@@ -440,7 +414,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
sunset = data.get('sunset', '')
|
||||
if cloud < 20 and "晴" in text_day:
|
||||
tips.append(f"<EFBFBD> 傍晚{sunset}左右可能有美丽的晚霞哦。")
|
||||
|
||||
|
||||
return tips
|
||||
|
||||
def _get_fun_stats(self, data: dict) -> str:
|
||||
@@ -448,10 +422,10 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
sunrise = data.get('sunrise', '-')
|
||||
sunset = data.get('sunset', '-')
|
||||
moon_phase = data.get('moonPhase', '')
|
||||
moon_icon = data.get('moonPhaseIcon', '') # 假设API有这个,或者直接用 phase
|
||||
|
||||
moon_icon = data.get('moonPhaseIcon', '') # 假设API有这个,或者直接用 phase
|
||||
|
||||
stats = []
|
||||
|
||||
|
||||
# 计算白昼时长
|
||||
try:
|
||||
sr_h, sr_m = map(int, sunrise.split(':'))
|
||||
@@ -471,14 +445,14 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
def _get_clothing_advice(self, t_min: int, t_max: int) -> str:
|
||||
"""根据最低和最高温综合给出穿衣建议"""
|
||||
avg_temp = (t_min + t_max) / 2
|
||||
|
||||
|
||||
if t_min < -5:
|
||||
return "极寒!羽绒服+保暖内衣+围巾手套缺一不可。"
|
||||
elif t_min < 5:
|
||||
return "很冷,建议穿棉衣、羽绒服,里面穿毛衣。"
|
||||
elif t_min < 12:
|
||||
return "较冷,大衣或厚外套是标配。"
|
||||
|
||||
|
||||
if avg_temp < 18:
|
||||
return "凉爽,建议风衣、卫衣或薄外套,早晚保暖。"
|
||||
elif avg_temp < 24:
|
||||
@@ -547,7 +521,7 @@ class WeatherPlugin(MessagePluginInterface):
|
||||
data = today_fc.copy()
|
||||
# 补充 date 字段,方便后续处理
|
||||
data["date"] = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
self.redis_manager.save_history(city_id, data)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"存档失败: {e}")
|
||||
|
||||
Reference in New Issue
Block a user