chore: sync current WechatHookBot workspace
This commit is contained in:
16
plugins/News60s/config.toml
Normal file
16
plugins/News60s/config.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# 60秒读懂世界插件配置
|
||||
|
||||
[proxy]
|
||||
enabled = true
|
||||
type = "socks5"
|
||||
host = "38.55.107.103"
|
||||
port = 53054
|
||||
|
||||
[behavior]
|
||||
enable_command = true # 是否启用指令触发
|
||||
command_keywords = ["/60s", "/新闻", "/早报"] # 触发指令
|
||||
group_whitelist = ["47652853273@chatroom", "48712602520@chatroom", "48977668554@chatroom", "51803330518@chatroom"] # 群组白名单,留空表示所有群组
|
||||
|
||||
[schedule]
|
||||
enabled = true # 是否启用定时推送
|
||||
# 定时任务在代码中配置为每天早上8点
|
||||
@@ -1,9 +1,9 @@
|
||||
"""
|
||||
60秒读懂世界插件
|
||||
|
||||
每日新闻推送和指令查询
|
||||
"""
|
||||
|
||||
"""
|
||||
60秒读懂世界插件
|
||||
|
||||
每日新闻推送和指令查询
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
@@ -12,31 +12,31 @@ from utils.plugin_base import PluginBase
|
||||
from utils.decorators import on_text_message, schedule
|
||||
from WechatHook import WechatHookClient
|
||||
import tomllib
|
||||
|
||||
|
||||
class News60s(PluginBase):
|
||||
"""60秒读懂世界插件"""
|
||||
|
||||
description = "60秒读懂世界 - 每日新闻推送"
|
||||
author = "ShiHao"
|
||||
version = "1.0.0"
|
||||
|
||||
|
||||
|
||||
class News60s(PluginBase):
|
||||
"""60秒读懂世界插件"""
|
||||
|
||||
description = "60秒读懂世界 - 每日新闻推送"
|
||||
author = "ShiHao"
|
||||
version = "1.0.0"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.config = None
|
||||
self.images_dir = None
|
||||
|
||||
async def async_init(self):
|
||||
"""异步初始化"""
|
||||
config_path = Path(__file__).parent / "config.toml"
|
||||
with open(config_path, "rb") as f:
|
||||
self.config = tomllib.load(f)
|
||||
|
||||
|
||||
async def async_init(self):
|
||||
"""异步初始化"""
|
||||
config_path = Path(__file__).parent / "config.toml"
|
||||
with open(config_path, "rb") as f:
|
||||
self.config = tomllib.load(f)
|
||||
|
||||
self.images_dir = Path(__file__).parent / "images"
|
||||
self.images_dir.mkdir(exist_ok=True)
|
||||
|
||||
logger.success("60秒读懂世界插件初始化完成")
|
||||
|
||||
|
||||
async def fetch_news_image(self) -> str:
|
||||
"""获取60秒新闻图片"""
|
||||
try:
|
||||
@@ -66,149 +66,154 @@ class News60s(PluginBase):
|
||||
except Exception as e:
|
||||
logger.error(f"获取60秒新闻失败: {e}")
|
||||
return None
|
||||
|
||||
def is_target_group(self, from_wxid: str) -> bool:
|
||||
"""检查是否是目标群组"""
|
||||
whitelist = self.config["behavior"].get("group_whitelist", [])
|
||||
# 空白名单表示所有群组
|
||||
if not whitelist:
|
||||
return True
|
||||
return from_wxid in whitelist
|
||||
|
||||
@on_text_message(priority=70)
|
||||
async def handle_message(self, bot: WechatHookClient, message: dict):
|
||||
"""处理文本消息"""
|
||||
if not self.config["behavior"]["enable_command"]:
|
||||
return True
|
||||
|
||||
content = message.get("Content", "").strip()
|
||||
from_wxid = message.get("FromWxid", "")
|
||||
is_group = message.get("IsGroup", False)
|
||||
|
||||
# 只处理群聊
|
||||
if not is_group:
|
||||
return True
|
||||
|
||||
# 检查是否是触发指令(支持 "@机器人 /60s" 或直接 "/60s")
|
||||
keywords = self.config["behavior"]["command_keywords"]
|
||||
matched = False
|
||||
for keyword in keywords:
|
||||
if content == keyword or content.endswith(f" {keyword}"):
|
||||
matched = True
|
||||
break
|
||||
|
||||
if not matched:
|
||||
return True
|
||||
|
||||
logger.info(f"收到60秒新闻请求: {from_wxid}")
|
||||
|
||||
try:
|
||||
image_path = await self.fetch_news_image()
|
||||
if image_path:
|
||||
await bot.send_image(from_wxid, image_path)
|
||||
logger.success(f"已发送60秒新闻到: {from_wxid}")
|
||||
else:
|
||||
await bot.send_text(from_wxid, "❌ 获取新闻失败,请稍后重试")
|
||||
except Exception as e:
|
||||
logger.error(f"发送60秒新闻失败: {e}")
|
||||
await bot.send_text(from_wxid, f"❌ 发送失败: {str(e)}")
|
||||
|
||||
return False
|
||||
|
||||
@schedule('cron', hour=8, minute=0)
|
||||
async def scheduled_news(self, bot=None):
|
||||
"""定时推送新闻"""
|
||||
if not self.config["schedule"]["enabled"]:
|
||||
return
|
||||
|
||||
logger.info("开始定时推送60秒新闻")
|
||||
|
||||
try:
|
||||
image_path = await self.fetch_news_image()
|
||||
if not image_path:
|
||||
logger.error("定时任务:获取新闻图片失败")
|
||||
return
|
||||
|
||||
# 获取bot实例
|
||||
if not bot:
|
||||
from utils.plugin_manager import PluginManager
|
||||
bot = PluginManager().bot
|
||||
|
||||
if not bot:
|
||||
logger.error("定时任务:无法获取bot实例")
|
||||
return
|
||||
|
||||
# 获取所有群聊
|
||||
import tomllib
|
||||
with open("main_config.toml", "rb") as f:
|
||||
main_config = tomllib.load(f)
|
||||
|
||||
# 推送到目标群组
|
||||
whitelist = self.config["behavior"].get("group_whitelist", [])
|
||||
|
||||
if whitelist:
|
||||
# 有白名单,只推送到白名单群组
|
||||
target_groups = whitelist
|
||||
else:
|
||||
# 无白名单,推送到所有群聊(需要从数据库或其他地方获取)
|
||||
# 这里暂时只推送到白名单,避免骚扰
|
||||
logger.warning("未配置群组白名单,跳过定时推送")
|
||||
return
|
||||
|
||||
success_count = 0
|
||||
for group_id in target_groups:
|
||||
try:
|
||||
await bot.send_image(group_id, image_path)
|
||||
success_count += 1
|
||||
logger.info(f"已推送60秒新闻到: {group_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"推送到 {group_id} 失败: {e}")
|
||||
|
||||
logger.success(f"定时推送完成,成功: {success_count}/{len(target_groups)}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"定时推送60秒新闻失败: {e}")
|
||||
|
||||
|
||||
def is_target_group(self, from_wxid: str) -> bool:
|
||||
"""检查是否是目标群组"""
|
||||
whitelist = self.config["behavior"].get("group_whitelist", [])
|
||||
# 空白名单表示所有群组
|
||||
if not whitelist:
|
||||
return True
|
||||
return from_wxid in whitelist
|
||||
|
||||
@on_text_message(priority=70)
|
||||
async def handle_message(self, bot: WechatHookClient, message: dict):
|
||||
"""处理文本消息"""
|
||||
if not self.config["behavior"]["enable_command"]:
|
||||
return True
|
||||
|
||||
content = message.get("Content", "").strip()
|
||||
from_wxid = message.get("FromWxid", "")
|
||||
is_group = message.get("IsGroup", False)
|
||||
|
||||
# 只处理群聊
|
||||
if not is_group:
|
||||
return True
|
||||
|
||||
# 检查是否是触发指令(支持 "@机器人 /60s" 或直接 "/60s")
|
||||
keywords = self.config["behavior"]["command_keywords"]
|
||||
matched = False
|
||||
for keyword in keywords:
|
||||
if content == keyword or content.endswith(f" {keyword}"):
|
||||
matched = True
|
||||
break
|
||||
|
||||
if not matched:
|
||||
return True
|
||||
|
||||
logger.info(f"收到60秒新闻请求: {from_wxid}")
|
||||
|
||||
try:
|
||||
image_path = await self.fetch_news_image()
|
||||
if image_path:
|
||||
await bot.send_image(from_wxid, image_path)
|
||||
logger.success(f"已发送60秒新闻到: {from_wxid}")
|
||||
else:
|
||||
await bot.send_text(from_wxid, "❌ 获取新闻失败,请稍后重试")
|
||||
except Exception as e:
|
||||
logger.error(f"发送60秒新闻失败: {e}")
|
||||
await bot.send_text(from_wxid, f"❌ 发送失败: {str(e)}")
|
||||
|
||||
return False
|
||||
|
||||
@schedule('cron', hour=8, minute=0)
|
||||
async def scheduled_news(self, bot=None):
|
||||
"""定时推送新闻"""
|
||||
if not self.config["schedule"]["enabled"]:
|
||||
return
|
||||
|
||||
logger.info("开始定时推送60秒新闻")
|
||||
|
||||
try:
|
||||
image_path = await self.fetch_news_image()
|
||||
if not image_path:
|
||||
logger.error("定时任务:获取新闻图片失败")
|
||||
return
|
||||
|
||||
# 获取bot实例
|
||||
if not bot:
|
||||
from utils.plugin_manager import PluginManager
|
||||
bot = PluginManager().bot
|
||||
|
||||
if not bot:
|
||||
logger.error("定时任务:无法获取bot实例")
|
||||
return
|
||||
|
||||
# 获取所有群聊
|
||||
import tomllib
|
||||
with open("main_config.toml", "rb") as f:
|
||||
main_config = tomllib.load(f)
|
||||
|
||||
# 推送到目标群组
|
||||
whitelist = self.config["behavior"].get("group_whitelist", [])
|
||||
|
||||
if whitelist:
|
||||
# 有白名单,只推送到白名单群组
|
||||
target_groups = whitelist
|
||||
else:
|
||||
# 无白名单,推送到所有群聊(需要从数据库或其他地方获取)
|
||||
# 这里暂时只推送到白名单,避免骚扰
|
||||
logger.warning("未配置群组白名单,跳过定时推送")
|
||||
return
|
||||
|
||||
success_count = 0
|
||||
for group_id in target_groups:
|
||||
try:
|
||||
await bot.send_image(group_id, image_path)
|
||||
success_count += 1
|
||||
logger.info(f"已推送60秒新闻到: {group_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"推送到 {group_id} 失败: {e}")
|
||||
|
||||
logger.success(f"定时推送完成,成功: {success_count}/{len(target_groups)}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"定时推送60秒新闻失败: {e}")
|
||||
|
||||
def get_llm_tools(self):
|
||||
"""返回LLM工具定义"""
|
||||
return [{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_daily_news",
|
||||
"description": "仅当用户明确要求“今日新闻/每日新闻/60秒新闻/早报”时调用;不要在闲聊或非新闻问题中触发。",
|
||||
"description": (
|
||||
"获取并发送当日 60 秒新闻图片/早报。"
|
||||
"仅当用户明确要求今日新闻、每日新闻、60秒新闻、早报时调用;"
|
||||
"普通闲聊或与新闻无关的问题不要触发。"
|
||||
),
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
"required": [],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
async def execute_llm_tool(self, tool_name: str, arguments: dict, bot: WechatHookClient, from_wxid: str) -> dict:
|
||||
"""执行LLM工具调用"""
|
||||
if tool_name != "get_daily_news":
|
||||
return None
|
||||
|
||||
try:
|
||||
logger.info(f"LLM工具调用60秒新闻: {from_wxid}")
|
||||
|
||||
image_path = await self.fetch_news_image()
|
||||
if image_path:
|
||||
await bot.send_image(from_wxid, image_path)
|
||||
return {
|
||||
"success": True,
|
||||
"message": "已获取并发送今日新闻图片",
|
||||
"no_reply": True # 已发送图片,不需要AI再回复
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "获取新闻图片失败,请稍后重试"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LLM工具执行失败: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"执行失败: {str(e)}"
|
||||
}
|
||||
|
||||
async def execute_llm_tool(self, tool_name: str, arguments: dict, bot: WechatHookClient, from_wxid: str) -> dict:
|
||||
"""执行LLM工具调用"""
|
||||
if tool_name != "get_daily_news":
|
||||
return None
|
||||
|
||||
try:
|
||||
logger.info(f"LLM工具调用60秒新闻: {from_wxid}")
|
||||
|
||||
image_path = await self.fetch_news_image()
|
||||
if image_path:
|
||||
await bot.send_image(from_wxid, image_path)
|
||||
return {
|
||||
"success": True,
|
||||
"message": "已获取并发送今日新闻图片",
|
||||
"no_reply": True # 已发送图片,不需要AI再回复
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "获取新闻图片失败,请稍后重试"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LLM工具执行失败: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"执行失败: {str(e)}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user