Skip to content

Commit d0fbfc7

Browse files
committed
feat(core): 重构消息发送模块并添加富媒体支持
- 重构了 MessageSenderBasePayload 类,增加了对富媒体消息的支持 - 新增了 MarkdownPayload、ArkPayload 和 MediaPayload 类 - 更新了消息发送函数,支持发送富媒体消息 - 添加了文件上传功能 - 优化了日志格式 Signed-off-by: Shanshui2024 <[email protected]>
1 parent ab7d9e5 commit d0fbfc7

33 files changed

+2475
-235
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# AxTBot-v2.1
33

44
一个基于 Python 的可扩展 Bot 框架。
5-
📖 完整文档请访问:[AxTBot-v2 | AxT Docs](https://docs.axtn.net/axtbot/v2.1/)
5+
📖 完整文档请访问:[AxTBot-v2 | AxT Docs](https://docs.axtn.net/axtbot/v2.1/guide/intro.html)
66

77
---
88

99
> [!CAUTION]
10-
> ⚠️ 当前分支正在进行上线测试,可能存在问题。若存在问题请立即报告给管理员。
10+
> ⚠️ 当前分支正在进行灰度测试,可能存在问题。若存在问题请立即报告给管理员。
1111
1212
---
1313

@@ -45,7 +45,7 @@ py -m venv .venv
4545
### 3️⃣ 安装依赖
4646

4747
```bash
48-
pip install -e .
48+
pip install -e .[standard]
4949
```
5050

5151
### 4️⃣ 配置环境变量
@@ -64,7 +64,7 @@ py main.py
6464

6565
> 没错!现在你可以为你的 Bot 自行开发插件 ✨
6666
67-
开发指南请访问:[快速开发 - AxTBot-v2 | AxT Docs](https://docs.axtn.net/axtbot/v2.1/Developer/)
67+
开发指南请访问:[快速开发 - AxTBot-v2 | AxT Docs](https://docs.axtn.net/axtbot/v2.1/develop/intro.html)
6868

6969
---
7070

config.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Bot: # 机器人的相关信息
2-
qq: 1234567890 # 机器人QQ号
3-
appid: 123456789 # 机器人APPID
2+
qq: 1234567890 # 机器人QQ号
3+
nickname: AxTBot-Public # 机器人昵称
4+
appid: 123456789 # 机器人APPID
45
token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # 机器人TOKEN
56
appsecret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # 机器人APPSECRET
67

@@ -14,7 +15,7 @@ Network: # 框架网络配置
1415
ssl_path: data # 证书存放路径 会自动在该路径下寻找*.pem文件
1516
# 请确保存在公钥和私钥(两个文件 公钥 cert.pem 私钥 key.pem)
1617

17-
webui: false # 是否启用WebUI
18+
webui: true # 是否启用WebUI
1819
# 请注意:启动WebUI后,请使用AxT提供的后台登陆服务(或您可以自行前往我们的公开存库中下载前端源码)
1920
# WebUI也会与Webhook的端口号一致
2021

main.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
import os, threading, datetime
2-
from fastapi import Request
1+
import os, threading, datetime, sys, asyncio
2+
from fastapi.middleware.cors import CORSMiddleware
3+
from fastapi.exceptions import RequestValidationError
34
from starlette.exceptions import HTTPException as StarletteHTTPException
4-
from starlette.middleware.sessions import SessionMiddleware
55
from tortoise.contrib.fastapi import register_tortoise
66

77
from src.app import create_app
88
from src.app.routes import router as main_router
99
from src.app.exceptions import (
1010
custom_http_exception_handler,
1111
universal_exception_handler,
12+
custom_validation_exception_handler,
1213
)
1314

1415
from src.Utils.Config import config # 导入配置模块
1516
from src.Utils.HeartBeat import heartbeat_check_start
1617
from src.Utils.MessageState import AppState
1718

19+
if sys.platform == "win32":
20+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
1821
APP_START_TIME = datetime.datetime.now()
1922
AppState.set_start_time(APP_START_TIME)
2023
app = create_app()
@@ -27,6 +30,7 @@
2730
app.include_router(main_router)
2831
app.add_exception_handler(StarletteHTTPException, custom_http_exception_handler)
2932
app.add_exception_handler(Exception, universal_exception_handler)
33+
app.add_exception_handler(RequestValidationError, custom_validation_exception_handler)
3034

3135
uvicorn_config = {
3236
"app": "main:app",
@@ -38,15 +42,14 @@
3842
}
3943

4044

41-
app.add_middleware(SessionMiddleware, secret_key=config.Advanced.session_secret)
42-
@app.middleware("http")
43-
async def session_middleware(request: Request, call_next):
44-
# 初始化会话
45-
if not hasattr(request.state, "session"):
46-
request.state.session = {}
4745

48-
response = await call_next(request)
49-
return response
46+
app.add_middleware(
47+
CORSMiddleware,
48+
allow_origins=["*"], # 前端开发服务器地址
49+
allow_credentials=True,
50+
allow_methods=["*"],
51+
allow_headers=["*"],
52+
)
5053

5154
if __name__ == "__main__":
5255
if not os.path.exists("data"):
@@ -86,4 +89,10 @@ async def session_middleware(request: Request, call_next):
8689
logger.warning("请注意:HTTP协议下,开放平台无法发包至本框架,造成消息不可达,请在生产环境中启用SSL")
8790
logger.info("正在启动守护线程...")
8891
threading.Thread(target=heartbeat_check_start, daemon=True, name="心跳检查线程").start()
89-
threading.Thread(target=uvicorn.run(**uvicorn_config), daemon=False, name="Uvicorn主线程").start() # 启动Uvicorn服务器
92+
uvicorn.run(
93+
loop="asyncio",
94+
http="httptools" if sys.platform != "win32" else "auto",
95+
timeout_keep_alive=1, # 空闲连接1秒关闭
96+
timeout_graceful_shutdown=1, # 关闭时等待1秒
97+
**uvicorn_config
98+
)

plugins/axt_plugin_atinfo.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"name": "[官方插件]获取框架信息",
1010
"version": "1.0.0",
1111
"author": "AxT-Team",
12-
"description": "获取当前框架的消息数等信息"
12+
"description": "获取当前框架的消息数等信息",
13+
"official": True
1314
}
1415

1516

@@ -22,10 +23,9 @@ async def get_message(event: GroupMessageEvent):
2223
days = elapsed_time.days
2324
hours, remainder = divmod(elapsed_time.seconds, 3600)
2425
minutes, seconds = divmod(remainder, 60)
25-
2626

2727
info = await get_system_info()
28-
reply = "\nAxTBot Public v 2.1\n" + \
28+
reply = "AxTBot Public v 2.1\n" + \
2929
"===============" + "\n" + \
3030
"CPU: " + info["cpu_usage"] + "\n" + \
3131
"RAM: " + info["ram_usage"] + "\n" + \

plugins/axt_plugin_cyberbook.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
1-
import aiohttp, asyncio
2-
from aiohttp import ClientError
3-
41
from src.Utils.PluginBase import command
5-
from src.Utils.EventClass import MessageEventPayload
6-
from src.Utils.Logger import logger
2+
from src.Utils.EventClass import GroupMessageEvent
3+
4+
from uapis_extension import post_for_api
75

86

97
__metadata__ = {
108
"name": "[官方插件]赛博之书",
119
"version": "1.0.0",
1210
"author": "AxT-Team",
13-
"description": "从接口请求得到赛博之书的答案~"
11+
"description": "从接口请求得到赛博之书的答案~",
12+
"official": True,
1413
}
1514

1615
@command(["ask", "/ask"])
17-
async def ask_handler(event: MessageEventPayload):
18-
url = f"https://uapis.cn/api/v1/answerbook/ask"
19-
async with aiohttp.ClientSession() as session:
20-
try:
21-
async with session.post(url) as response:
22-
response.raise_for_status()
23-
result = (await response.json()).get("answer")
24-
await event.reply(result)
25-
except (ClientError, asyncio.TimeoutError) as e:
26-
logger.error(f"请求错误: {e}")
27-
await event.reply("答案获取失败,可能是偶然错误。\n我们已将报错信息发送给管理员,您可以稍后重新尝试")
16+
async def ask_handler(event: GroupMessageEvent):
17+
try:
18+
response = await post_for_api("/api/v1/answerbook/ask")
19+
except Exception as e:
20+
await event.reply("答案获取失败,可能是偶然错误。\n我们已将报错信息发送给管理员,您可以稍后重新尝试")
21+
raise
22+
returns = response["answer"] if "answer" in response else "赛博之书沉默了..."
23+
await event.reply(returns)

plugins/axt_plugin_help.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
"name": "[官方插件]帮助插件",
66
"version": "1.0.0",
77
"author": "AxT-Team",
8-
"description": "获取帮助菜单"
8+
"description": "获取帮助菜单",
9+
"official": True,
910
}
1011

1112

1213
@command(["help", "/help"])
1314
async def help_handler(event: MessageEventPayload):
14-
content = "\n=======AxT社区机器人=======" + "\n" + \
15+
content = "=======AxT社区机器人=======" + "\n" + \
1516
"| help | - 获取帮助菜单" + "\n" + \
1617
"| ping | - 显示Ping菜单" + "\n" + \
1718
"| ipinfo | - 显示IPInfo菜单" + "\n" + \
@@ -25,4 +26,4 @@ async def help_handler(event: MessageEventPayload):
2526
"官方社区群: 832275338" + "\n" + \
2627
"===============" + "\n" + \
2728
"AxTBot Public v 2.1"
28-
await event.reply(content)
29+
await event.reply(content)

plugins/axt_plugin_hotlist.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from src.Utils.PluginBase import command
2+
from src.Utils.EventClass import MessageEventPayload
3+
4+
from uapis_extension import format_hot_search, get_from_api
5+
6+
__metadata__ = {
7+
"name": "[官方插件]获取今日热榜",
8+
"version": "1.0.0",
9+
"author": "AxT-Team",
10+
"description": "获取多平台热榜信息",
11+
"official": True,
12+
}
13+
14+
@command(["hotlist", "/hotlist"])
15+
async def hotlist_handler(event: MessageEventPayload):
16+
content = "=======每日热榜菜单=======" + "\n" + \
17+
"/hotlist [热榜类型] - 查询指定热榜信息" + "\n" + \
18+
"可选的热榜类型有:" + "\n" + \
19+
"- acfun | AcFun热搜榜" + "\n" + \
20+
"- weibo | 微博热搜榜" + "\n" + \
21+
"- bilibili | 哔哩哔哩全站日榜" + "\n" + \
22+
"- douyin | 抖音热搜榜" + "\n" + \
23+
"- zhihu | 知乎热搜榜" + "\n" + \
24+
"- douyin | 抖音热搜榜" + "\n" + \
25+
"==========================" + "\n" + \
26+
"使用示例: /hotlist weibo" + "\n" + \
27+
"注:如果指令发送后无返回且无获取错误信息,视为热榜内含有违规信息,被QQ消息审核拦截" + "\n" + \
28+
"=========================="
29+
if event.content.startswith(("/hotlist ","hotlist ")) and event.content.split(" ")[1]:
30+
hot_list = None
31+
hot_type = None
32+
msg = event.content
33+
async def get_hot_list(hot_type: str) -> dict:
34+
returns = await get_from_api(f"/api/v1/misc/hotboard?type={hot_type}")
35+
return returns
36+
if msg.split(" ")[1] == "bilibili":
37+
hot_list = await get_hot_list("bilibili")
38+
hot_type = "B站-日榜"
39+
elif msg.split(" ")[1] == "acfun":
40+
hot_list = await get_hot_list("acfun")
41+
hot_type = "A站-热搜榜"
42+
elif msg.split(" ")[1] == "weibo":
43+
hot_list = await get_hot_list("weibo")
44+
hot_type = "微博-热搜榜"
45+
elif msg.split(" ")[1] == "zhihu":
46+
hot_list = await get_hot_list("zhihu")
47+
hot_type = "知乎-热搜榜"
48+
elif msg.split(" ")[1] == "douyin":
49+
hot_list = await get_hot_list("douyin")
50+
hot_type = "抖音-热搜榜"
51+
else:
52+
await event.reply('请指定热榜类型')
53+
return
54+
55+
if hot_list is None:
56+
await event.reply('未查询到该热搜信息')
57+
return
58+
else:
59+
content = "===" + hot_type + "===" + "\n" + \
60+
format_hot_search(hot_list) + "\n" + \
61+
"=============" + "\n" + \
62+
hot_list["update_time"] + "\n" + \
63+
"============="
64+
await event.reply(content)
65+
else:
66+
await event.reply(content)
67+
68+
69+

plugins/axt_plugin_ipinfo.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from src.Utils.PluginBase import command
2+
from src.Utils.EventClass import MessageEventPayload
3+
4+
from uapis_extension import get_from_api
5+
6+
__metadata__ = {
7+
"name": "[官方插件]获取IP地址信息",
8+
"version": "1.0.0",
9+
"author": "AxT-Team",
10+
"description": "获取IP地址信息",
11+
"official": True,
12+
}
13+
14+
@command(["ipinfo", "/ipinfo"])
15+
async def ipinfo_handler(event: MessageEventPayload):
16+
if event.content.split(" ")[1]:
17+
host = event.content.split(" ")[1]
18+
info = await get_from_api(f"api/v1/network/ipinfo?ip={host}")
19+
if info is None:
20+
await event.reply('未查询到该IP信息')
21+
else:
22+
contents = "=====IP信息=====" + "\n" + \
23+
"IP: " + info["ip"] + "\n" + \
24+
"| 开始 IP: " + info["beginip"] + "\n" + \
25+
"| 结束 IP: " + info["endip"] + "\n" + \
26+
"| 归属地: " + info["region"] + "\n" + \
27+
"| 纬度: " + str(info["latitude"]) + "\n" + \
28+
"| 经度: " + str(info["longitude"]) + "\n" + \
29+
"| ISP: " + str(info["isp"]) + "\n" + \
30+
"| LLC: " + info["llc"] + "\n" + \
31+
"| ASN: " + str(info["asn"]) + "\n" + \
32+
"=============="
33+
await event.reply(contents)
34+
else:
35+
contents = "=======IPInfo查询菜单=======" + "\n" + \
36+
"/ipinfo [IP] - 查询IP详细信息" + "\n" + \
37+
"==========================" + "\n" + \
38+
"使用示例: /ipinfo 1.1.1.1" + "\n" + \
39+
"=========================="
40+
await event.reply(contents)

0 commit comments

Comments
 (0)