这是alicebot文件夹下大致的文件所在位置
.
├── plugins
│ ├── a.py (单个 Python 文件)
│ └── b (Python 包)
│ └── __init__.py
├── config.toml
└── main.py
首先聊一下插件类;
插件的python代码 主要放在plugins文件夹下(建议写单独的python文件)
每个plugin代码都必须实现rule()和handle()功能
rule()方法用于定义插件的触发规则,handle()方法当符合插件规则时会被调用,同时每个插件类都必须是plugin的子类。
from alicebot import Plugin
class HalloAlice(Plugin):
priority: int = 0
block: bool = False
async def handle(self) -> None:
pass
async def rule(self) -> bool:
return True
priority:表示插件运行的优先级 数字越小优先级越高
block: 表示当前插件执行结束后是否阻止事件的传播,如果设置为 True
,则当前插件执行完毕后会停止当前事件传播,比当前插件优先级低的插件将不会被执行
它会先运行rule()方法 根据返回值判断是否要运行handle()方法
以下是一些内置方法和属性
from alicebot import Plugin
from alicebot.exceptions import GetEventTimeout
class HalloAlice(Plugin):
async def handle(self) -> None:
try:
name_event = await self.event.ask("What is you name?", timeout=10)
# await self.event.reply("What is you name?")
# name_event = await self.event.get(timeout=10)
# await self.event.reply("What is you name?")
# name_event = await self.bot.get(
# lambda event: (
# event.adapter.name == "cqhttp"
# and event.type == "message"
# and event.sender.user_id == self.event.sender.user_id
# ),
# timeout=10,
# )
except GetEventTimeout:
return
else:
await self.event.reply(f"Hello, {name_event.message}!")
async def rule(self) -> bool:
if self.event.adapter.name != "cqhttp":
return False
if self.event.type != "message":
return False
return str(self.event.message).lower() == "hello"
ask()和get()方法 都可以指定超时时间,因为都是异步方法 所以需要使用await
上述代码中 else用于try语句成功执行,触发条件是你发送hello,它会发送信息问你的姓名,然后返回"hello+姓名"
输出日志一般使用logger.info("Hello"),不建议使用print
例子:(一个完整的可以查询天气的插件)
import aiohttp
from alicebot import Plugin
from alicebot.exceptions import GetEventTimeout
class Weather(Plugin):
async def handle(self) -> None:
args = self.event.get_plain_text().split(" ")
if len(args) >= 2:
await self.event.reply(await self.get_weather(args[1]))
return
try:
city_event = await self.event.ask("请输入想要查询天气的城市:", timeout=10)
except GetEventTimeout:
return
else:
await self.event.reply(await self.get_weather(city_event.get_plain_text()))
async def rule(self) -> bool:
if self.event.adapter.name != "cqhttp":
return False
if self.event.type != "message":
return False
return self.event.message.startswith("天气") or self.event.message.startswith("weather")
@staticmethod
async def get_weather(city):
# 使用和风天气API获取城市ID
api_key = "c7572bff778844748f0b2c955aa83e31" # 请替换为你的实际API Key
location_api_url = f"https://geoapi.qweather.com/v2/city/lookup?location={city}&key={api_key}"
async with aiohttp.ClientSession() as session:
async with session.get(location_api_url) as response:
if response.status != 200:
return "无法获取城市信息,请稍后再试。"
location_data = await response.json()
if location_data.get("code") != "200":
return "获取城市信息失败,请稍后再试。"
location_id = location_data["location"][0]["id"]
weather_api_url = f"https://devapi.qweather.com/v7/weather/now?location={location_id}&key={api_key}"
async with session.get(weather_api_url) as response:
if response.status != 200:
return "无法获取天气信息,请稍后再试。"
weather_data = await response.json()
if weather_data.get("code") != "200":
return "获取天气信息失败,请稍后再试。"
# 提取天气信息
now = weather_data.get("now", {})
weather_text = now.get("text", "未知")
temperature = now.get("temp", "未知")
feels_like = now.get("feelsLike", "未知")
wind_dir = now.get("windDir", "未知")
return (
f"{city}的天气是:{weather_text},"
f"温度:{temperature}°C,"
f"体感温度:{feels_like}°C,"
f"风向:{wind_dir}。"
)
简单的报时功能的案例
import asyncio
import logging
import json
import urllib3
from datetime import datetime, timedelta
from alicebot import Plugin
# 配置日志
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
class Time(Plugin):
# 配置群组ID列表
CONFIG_GROUPS = [591910269]
# 指定报时的时间点,格式为 (小时, 分钟)
REPORT_TIMES = [(8, 0), (12, 0), (18, 0), (23, 0)]
async def handle(self) -> None:
"""
插件的处理方法,AliceBot 会在插件触发时调用此方法。
"""
logger.debug("Time plugin handle method called.")
# 初始化并启动周期任务
asyncio.create_task(self.scheduled_task())
async def scheduled_task(self):
"""
定时任务,每到指定时间点发送报时消息。
"""
while True:
now = datetime.now()
# 计算下一个报时点
next_report_time = self.get_next_report_time(now)
wait_seconds = (next_report_time - now).total_seconds()
logger.debug(f"Next report time at {next_report_time}, waiting for {wait_seconds} seconds.")
await asyncio.sleep(wait_seconds)
# 获取当前时间并格式化为字符串
now = datetime.now()
time_str = now.strftime("%H:%M")
message_content = f"报时:现在是{time_str}。"
logger.debug(f"Sending message: {message_content}")
# 发送报时消息到配置的群组
for group_id in self.CONFIG_GROUPS:
try:
# 使用 urllib3 发送 POST 请求
http = urllib3.PoolManager()
headers = {
'Authorization': 'Bearer your_actual_access_token', # 替换为你的 API Token
'Content-Type': 'application/json'
}
data = {
'group_id': group_id,
'message': message_content
}
response = http.request(
'POST',
'http://127.0.0.1:5700/send_group_msg',
body=json.dumps(data),
headers=headers
)
if response.status == 200:
logger.info(f"Message sent to group {group_id}")
else:
logger.error(f"Failed to send message to group {group_id}: {response.data.decode('utf-8')}")
except Exception as e:
logger.error(f"Failed to send message to group {group_id}: {e}")
def get_next_report_time(self, now):
"""
获取下一个报时点。
参数:
now (datetime): 当前时间。
返回:
datetime: 下一个报时点。
"""
# 生成今天的所有报时点
today_times = [datetime(now.year, now.month, now.day, hour, minute) for hour, minute in self.REPORT_TIMES]
# 找出还未到的报时点
future_times = [t for t in today_times if t > now]
if future_times:
return future_times[0]
# 如果今天的报时点已经过去,则返回明天的第一个报时点
return today_times[0] + timedelta(days=1)
async def rule(self) -> bool:
"""
规则方法,用于判断是否触发插件。
返回:
bool: 总是返回 True,表示总是触发。
"""
return True
skip():用于跳过当前插件继续事件传播