diff --git a/.github/ISSUE_TEMPLATE/PLUGIN_PUBLISH.yml b/.github/ISSUE_TEMPLATE/PLUGIN_PUBLISH.yml
index e5aaaaf78..7eb5ae15c 100644
--- a/.github/ISSUE_TEMPLATE/PLUGIN_PUBLISH.yml
+++ b/.github/ISSUE_TEMPLATE/PLUGIN_PUBLISH.yml
@@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
- 欢迎发布插件到插件市场!
+ 欢迎发布插件到插件市场!请确保您的插件经过**完整的**测试。
- type: textarea
attributes:
@@ -22,9 +22,10 @@ body:
插件名:
插件作者:
插件简介:
- 标签: (可选)
- 社交链接: (可选, 将会在插件市场作者名称上作为可点击的链接)
- description: 必填。请以列表的字段按顺序将插件名、插件作者、插件简介放在这里。
+ 支持的消息平台:(必填,如 QQ、微信、飞书)
+ 标签:(可选)
+ 社交链接:(可选, 将会在插件市场作者名称上作为可点击的链接)
+ description: 必填。请以列表的字段按顺序将插件名、插件作者、插件简介放在这里。如果您不知道支持哪些消息平台,请填写测试过的消息平台。
- type: checkboxes
attributes:
diff --git a/.gitignore b/.gitignore
index 865b0596d..a3b2aad90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,5 +26,5 @@ venv/*
packages/python_interpreter/workplace
.venv/*
.conda/
-.idea/
+.idea
pytest.ini
diff --git a/README.md b/README.md
index b4d97fbbb..9d9589384 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,13 @@ _✨ 易上手的多平台 LLM 聊天机器人及开发框架 ✨_
-[](https://github.com/Soulter/AstrBot/releases/latest)
-
-
-
-[](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e)
-
-[](https://codecov.io/gh/Soulter/AstrBot)
-[](https://gitcode.com/Soulter/AstrBot)
+[](https://github.com/Soulter/AstrBot/releases/latest)
+
+
+
+[](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e)
+
+[](https://codecov.io/gh/Soulter/AstrBot)
English |
日本語 |
@@ -27,6 +26,8 @@ _✨ 易上手的多平台 LLM 聊天机器人及开发框架 ✨_
AstrBot 是一个松耦合、异步、支持多消息平台部署、具有易用的插件系统和完善的大语言模型(LLM)接入功能的聊天机器人及开发框架。
+[](https://gitcode.com/Soulter/AstrBot)
+
## ✨ 主要功能
1. **大语言模型对话**。支持各种大语言模型,包括 OpenAI API、Google Gemini、Llama、Deepseek、ChatGLM 等,支持接入本地部署的大模型,通过 Ollama、LLMTuner。具有多轮对话、人格情境、多模态能力,支持图片理解、语音转文字(Whisper)。
@@ -51,15 +52,19 @@ AstrBot 是一个松耦合、异步、支持多消息平台部署、具有易用
需要电脑上安装有 Python(>3.10)。请参阅官方文档 [使用 Windows 一键安装器部署 AstrBot](https://astrbot.app/deploy/astrbot/windows.html) 。
-#### Replit 部署
+#### 宝塔面板部署
-[](https://repl.it/github/Soulter/AstrBot)
+请参阅官方文档 [宝塔面板部署](https://astrbot.app/deploy/astrbot/btpanel.html) 。
#### CasaOS 部署
社区贡献的部署方式。
-请参阅官方文档 [通过源码部署 AstrBot](https://astrbot.app/deploy/astrbot/casaos.html) 。
+请参阅官方文档 [CasaOS 部署](https://astrbot.app/deploy/astrbot/casaos.html) 。
+
+#### Replit 部署
+
+[](https://repl.it/github/Soulter/AstrBot)
#### 手动部署
@@ -106,6 +111,7 @@ AstrBot 是一个松耦合、异步、支持多消息平台部署、具有易用
| Whisper | ✔ | 语音转文本 | 支持 API、本地部署 |
| SenseVoice | ✔ | 语音转文本 | 本地部署 |
| OpenAI TTS API | ✔ | 文本转语音 | |
+| GSVI | ✔ | 文本转语音 | GPT-Sovits-Inference |
| Fishaudio | ✔ | 文本转语音 | GPT-Sovits 作者参与的项目 |
| Edge-TTS | ✔ | 文本转语音 | Edge 浏览器的免费 TTS |
diff --git a/astrbot/api/platform/__init__.py b/astrbot/api/platform/__init__.py
index dcc02bb49..5a98c5903 100644
--- a/astrbot/api/platform/__init__.py
+++ b/astrbot/api/platform/__init__.py
@@ -5,6 +5,7 @@ from astrbot.core.platform import (
MessageMember,
MessageType,
PlatformMetadata,
+ Group,
)
from astrbot.core.platform.register import register_platform_adapter
@@ -18,4 +19,5 @@ __all__ = [
"MessageType",
"PlatformMetadata",
"register_platform_adapter",
+ "Group",
]
diff --git a/astrbot/core/platform/__init__.py b/astrbot/core/platform/__init__.py
index 48ea57b8a..4007b2d90 100644
--- a/astrbot/core/platform/__init__.py
+++ b/astrbot/core/platform/__init__.py
@@ -1,7 +1,7 @@
from .platform import Platform
from .astr_message_event import AstrMessageEvent
from .platform_metadata import PlatformMetadata
-from .astrbot_message import AstrBotMessage, MessageMember, MessageType
+from .astrbot_message import AstrBotMessage, MessageMember, MessageType, Group
__all__ = [
"Platform",
@@ -10,4 +10,5 @@ __all__ = [
"AstrBotMessage",
"MessageMember",
"MessageType",
+ "Group",
]
diff --git a/astrbot/core/platform/astr_message_event.py b/astrbot/core/platform/astr_message_event.py
index fceb63ce7..3e1b14ee6 100644
--- a/astrbot/core/platform/astr_message_event.py
+++ b/astrbot/core/platform/astr_message_event.py
@@ -1,11 +1,9 @@
import abc
import asyncio
from dataclasses import dataclass
-from .astrbot_message import AstrBotMessage
-from .platform_metadata import PlatformMetadata
-from astrbot.core.message.message_event_result import MessageEventResult, MessageChain
-from astrbot.core.platform.message_type import MessageType
-from typing import List, Union
+from typing import List, Union, Optional
+
+from astrbot.core.db.po import Conversation
from astrbot.core.message.components import (
Plain,
Image,
@@ -16,9 +14,12 @@ from astrbot.core.message.components import (
Forward,
Reply,
)
-from astrbot.core.utils.metrics import Metric
+from astrbot.core.message.message_event_result import MessageEventResult, MessageChain
+from astrbot.core.platform.message_type import MessageType
from astrbot.core.provider.entites import ProviderRequest
-from astrbot.core.db.po import Conversation
+from astrbot.core.utils.metrics import Metric
+from .astrbot_message import AstrBotMessage, Group
+from .platform_metadata import PlatformMetadata
@dataclass
@@ -201,15 +202,6 @@ class AstrMessageEvent(abc.ABC):
"""
return self.role == "admin"
- async def send(self, message: MessageChain):
- """
- 发送消息到消息平台。
- """
- asyncio.create_task(
- Metric.upload(msg_event_tick=1, adapter_name=self.platform_meta.name)
- )
- self._has_send_oper = True
-
async def _pre_send(self):
"""调度器会在执行 send() 前调用该方法"""
@@ -371,3 +363,26 @@ class AstrMessageEvent(abc.ABC):
system_prompt=system_prompt,
conversation=conversation,
)
+
+ """平台适配器"""
+
+ async def send(self, message: MessageChain):
+ """发送消息到消息平台。
+
+ Args:
+ message (MessageChain): 消息链,具体使用方式请参考文档。
+ """
+ asyncio.create_task(
+ Metric.upload(msg_event_tick=1, adapter_name=self.platform_meta.name)
+ )
+ self._has_send_oper = True
+
+ async def get_group(self, group_id: str = None, **kwargs) -> Optional[Group]:
+ """获取一个群聊的数据, 如果不填写 group_id: 如果是私聊消息,返回 None。如果是群聊消息,返回当前群聊的数据。
+
+ 适配情况:
+
+ - gewechat
+ - aiocqhttp(OneBotv11)
+ """
+ ...
diff --git a/astrbot/core/platform/astrbot_message.py b/astrbot/core/platform/astrbot_message.py
index ea55eaf4b..e7bd4bd9c 100644
--- a/astrbot/core/platform/astrbot_message.py
+++ b/astrbot/core/platform/astrbot_message.py
@@ -10,6 +10,41 @@ class MessageMember:
user_id: str # 发送者id
nickname: str = None
+ def __str__(self):
+ # 使用 f-string 来构建返回的字符串表示形式
+ return (
+ f"User ID: {self.user_id},"
+ f"Nickname: {self.nickname if self.nickname else 'N/A'}"
+ )
+
+
+@dataclass
+class Group:
+ group_id: str
+ """群号"""
+ group_name: str = None
+ """群名称"""
+ group_avatar: str = None
+ """群头像"""
+ group_owner: str = None
+ """群主 id"""
+ group_admins: List[str] = None
+ """群管理员 id"""
+ members: List[MessageMember] = None
+ """所有群成员"""
+
+ def __str__(self):
+ # 使用 f-string 来构建返回的字符串表示形式
+ return (
+ f"Group ID: {self.group_id}\n"
+ f"Name: {self.group_name if self.group_name else 'N/A'}\n"
+ f"Avatar: {self.group_avatar if self.group_avatar else 'N/A'}\n"
+ f"Owner ID: {self.group_owner if self.group_owner else 'N/A'}\n"
+ f"Admin IDs: {self.group_admins if self.group_admins else 'N/A'}\n"
+ f"Members Len: {len(self.members) if self.members else 0}\n"
+ f"First Member: {self.members[0] if self.members else 'N/A'}\n"
+ )
+
class AstrBotMessage:
"""
diff --git a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py
index 0dfe41a4e..c7aede7d1 100644
--- a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py
+++ b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py
@@ -1,6 +1,7 @@
import asyncio
-
+import typing
from astrbot.api.event import AstrMessageEvent, MessageChain
+from astrbot.api.platform import Group, MessageMember
from astrbot.api.message_components import Plain, Image, Record, At, Node, Nodes
from aiocqhttp import CQHttp
@@ -74,3 +75,46 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
await self.bot.send(self.message_obj.raw_message, ret)
await super().send(message)
+
+ async def get_group(self, group_id=None, **kwargs):
+ if isinstance(group_id, str) and group_id.isdigit():
+ group_id = int(group_id)
+ elif self.get_group_id():
+ group_id = int(self.get_group_id())
+ else:
+ return None
+
+ info: dict = await self.bot.call_action(
+ "get_group_info",
+ group_id=group_id,
+ )
+
+ members: typing.List[typing.Dict] = await self.bot.call_action(
+ "get_group_member_list",
+ group_id=group_id,
+ )
+
+ owner_id = None
+ admin_ids = []
+ for member in members:
+ if member["role"] == "owner":
+ owner_id = member["user_id"]
+ if member["role"] == "admin":
+ admin_ids.append(member["user_id"])
+
+ group = Group(
+ group_id=str(group_id),
+ group_name=info.get("group_name"),
+ group_avatar="",
+ group_admins=admin_ids,
+ group_owner=str(owner_id),
+ members=[
+ MessageMember(
+ user_id=member["user_id"],
+ nickname=member.get("nickname") or member.get("card"),
+ )
+ for member in members
+ ],
+ )
+
+ return group
diff --git a/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py b/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py
index 507b3bb50..b38c7d5c8 100644
--- a/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py
+++ b/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py
@@ -196,7 +196,10 @@ class DingtalkPlatformAdapter(Platform):
self._event_queue.put_nowait(event)
async def run(self):
- await self.client_.start()
+ # await self.client_.start()
+ loop = asyncio.get_event_loop()
+ # 钉钉的 SDK 并没有实现真正的异步,start() 里面有堵塞方法。
+ await loop.run_in_executor(None, lambda: asyncio.run(self.client_.start()))
def get_client(self):
return self.client
diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py
index 53b4fe276..d2f28f09d 100644
--- a/astrbot/core/platform/sources/gewechat/client.py
+++ b/astrbot/core/platform/sources/gewechat/client.py
@@ -1,17 +1,19 @@
-import threading
import asyncio
-import aiohttp
-import quart
import base64
import datetime
-import re
import os
+import re
+import threading
+
+import aiohttp
import anyio
-from astrbot.api.platform import AstrBotMessage, MessageMember, MessageType
-from astrbot.api.message_components import Plain, Image, At, Record
+import quart
+
from astrbot.api import logger, sp
-from .downloader import GeweDownloader
+from astrbot.api.message_components import Plain, Image, At, Record
+from astrbot.api.platform import AstrBotMessage, MessageMember, MessageType
from astrbot.core.utils.io import download_image_by_url
+from .downloader import GeweDownloader
class SimpleGewechatClient:
@@ -51,11 +53,11 @@ class SimpleGewechatClient:
self.server = quart.Quart(__name__)
self.server.add_url_rule(
- "/astrbot-gewechat/callback", view_func=self.callback, methods=["POST"]
+ "/astrbot-gewechat/callback", view_func=self._callback, methods=["POST"]
)
self.server.add_url_rule(
"/astrbot-gewechat/file/",
- view_func=self.handle_file,
+ view_func=self._handle_file,
methods=["GET"],
)
@@ -73,6 +75,7 @@ class SimpleGewechatClient:
self.stop = False
async def get_token_id(self):
+ """获取 Gewechat Token。"""
async with aiohttp.ClientSession() as session:
async with session.post(f"{self.base_url}/tools/getTokenId") as resp:
json_blob = await resp.json()
@@ -258,7 +261,7 @@ class SimpleGewechatClient:
logger.debug(f"abm: {abm}")
return abm
- async def callback(self):
+ async def _callback(self):
data = await quart.request.json
logger.debug(f"收到 gewechat 回调: {data}")
@@ -280,7 +283,7 @@ class SimpleGewechatClient:
return quart.jsonify({"r": "AstrBot ACK"})
- async def handle_file(self, file_id):
+ async def _handle_file(self, file_id):
file_path = f"data/temp/{file_id}"
return await quart.send_file(file_path)
@@ -306,17 +309,17 @@ class SimpleGewechatClient:
await self.server.run_task(
host="0.0.0.0",
port=self.port,
- shutdown_trigger=self.shutdown_trigger_placeholder,
+ shutdown_trigger=self._shutdown_trigger_placeholder,
)
- async def shutdown_trigger_placeholder(self):
+ async def _shutdown_trigger_placeholder(self):
# TODO: use asyncio.Event
while not self.event_queue.closed and not self.stop: # noqa: ASYNC110
await asyncio.sleep(1)
logger.info("gewechat 适配器已关闭。")
async def check_online(self, appid: str):
- # /login/checkOnline
+ """检查 APPID 对应的设备是否在线。"""
async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.base_url}/login/checkOnline",
@@ -327,6 +330,7 @@ class SimpleGewechatClient:
return json_blob["data"]
async def logout(self):
+ """登出 gewechat。"""
if self.appid:
online = await self.check_online(self.appid)
if online:
@@ -340,6 +344,7 @@ class SimpleGewechatClient:
logger.info(f"登出结果: {json_blob}")
async def login(self):
+ """登录 gewechat。一般来说插件用不到这个方法。"""
if self.token is None:
await self.get_token_id()
@@ -451,9 +456,18 @@ class SimpleGewechatClient:
self.appid = appid
logger.info(f"已保存 APPID: {appid}")
- """API"""
+ """API 部分。Gewechat 的 API 文档请参考: https://apifox.com/apidoc/shared/69ba62ca-cb7d-437e-85e4-6f3d3df271b1
+ """
- async def get_chatroom_member_list(self, chatroom_wxid: str):
+ async def get_chatroom_member_list(self, chatroom_wxid: str) -> dict:
+ """获取群成员列表。
+
+ Args:
+ chatroom_wxid (str): 微信群聊的id。可以通过 event.get_group_id() 获取。
+
+ Returns:
+ dict: 返回群成员列表字典。其中键为 memberList 的值为群成员列表。
+ """
payload = {"appId": self.appid, "chatroomId": chatroom_wxid}
async with aiohttp.ClientSession() as session:
@@ -466,6 +480,7 @@ class SimpleGewechatClient:
return json_blob["data"]
async def post_text(self, to_wxid, content: str, ats: str = ""):
+ """发送纯文本消息"""
payload = {
"appId": self.appid,
"toWxid": to_wxid,
@@ -482,6 +497,7 @@ class SimpleGewechatClient:
logger.debug(f"发送消息结果: {json_blob}")
async def post_image(self, to_wxid, image_url: str):
+ """发送图片消息"""
payload = {
"appId": self.appid,
"toWxid": to_wxid,
@@ -496,6 +512,12 @@ class SimpleGewechatClient:
logger.debug(f"发送图片结果: {json_blob}")
async def post_voice(self, to_wxid, voice_url: str, voice_duration: int):
+ """发送语音信息
+
+ Args:
+ voice_url (str): 语音文件的网络链接
+ voice_duration (int): 语音时长,毫秒
+ """
payload = {
"appId": self.appid,
"toWxid": to_wxid,
@@ -513,6 +535,13 @@ class SimpleGewechatClient:
logger.debug(f"发送语音结果: {json_blob}")
async def post_file(self, to_wxid, file_url: str, file_name: str):
+ """发送文件
+
+ Args:
+ to_wxid (string): 微信ID
+ file_url (str): 文件的网络链接
+ file_name (str): 文件名
+ """
payload = {
"appId": self.appid,
"toWxid": to_wxid,
@@ -526,3 +555,114 @@ class SimpleGewechatClient:
) as resp:
json_blob = await resp.json()
logger.debug(f"发送文件结果: {json_blob}")
+
+ async def add_friend(self, v3: str, v4: str, content: str):
+ """申请添加好友"""
+ payload = {
+ "appId": self.appid,
+ "scene": 3,
+ "content": content,
+ "v4": v4,
+ "v3": v3,
+ "option": 2,
+ }
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ f"{self.base_url}/contacts/addContacts",
+ headers=self.headers,
+ json=payload,
+ ) as resp:
+ json_blob = await resp.json()
+ logger.debug(f"申请添加好友结果: {json_blob}")
+ return json_blob
+
+ async def get_group(self, group_id: str):
+ payload = {
+ "appId": self.appid,
+ "chatroomId": group_id,
+ }
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ f"{self.base_url}/group/getChatroomInfo",
+ headers=self.headers,
+ json=payload,
+ ) as resp:
+ json_blob = await resp.json()
+ logger.debug(f"获取群信息结果: {json_blob}")
+ return json_blob
+
+ async def get_group_member(self, group_id: str):
+ payload = {
+ "appId": self.appid,
+ "chatroomId": group_id,
+ }
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ f"{self.base_url}/group/getChatroomMemberList",
+ headers=self.headers,
+ json=payload,
+ ) as resp:
+ json_blob = await resp.json()
+ logger.debug(f"获取群信息结果: {json_blob}")
+ return json_blob
+
+ async def accept_group_invite(self, url: str):
+ """同意进群"""
+ payload = {"appId": self.appid, "url": url}
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ f"{self.base_url}/group/agreeJoinRoom",
+ headers=self.headers,
+ json=payload,
+ ) as resp:
+ json_blob = await resp.json()
+ logger.debug(f"获取群信息结果: {json_blob}")
+ return json_blob
+
+ async def add_group_member_to_friend(
+ self, group_id: str, to_wxid: str, content: str
+ ):
+ payload = {
+ "appId": self.appid,
+ "chatroomId": group_id,
+ "content": content,
+ "memberWxid": to_wxid,
+ }
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ f"{self.base_url}/group/addGroupMemberAsFriend",
+ headers=self.headers,
+ json=payload,
+ ) as resp:
+ json_blob = await resp.json()
+ logger.debug(f"获取群信息结果: {json_blob}")
+ return json_blob
+
+ async def get_user_or_group_info(self, *ids):
+ """
+ 获取用户或群组信息。
+
+ :param ids: 可变数量的 wxid 参数
+ """
+
+ wxids_str = list(ids)
+
+ payload = {
+ "appId": self.appid,
+ "wxids": wxids_str, # 使用逗号分隔的字符串
+ }
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ f"{self.base_url}/contacts/getDetailInfo",
+ headers=self.headers,
+ json=payload,
+ ) as resp:
+ json_blob = await resp.json()
+ logger.debug(f"获取群信息结果: {json_blob}")
+ return json_blob
diff --git a/astrbot/core/platform/sources/gewechat/gewechat_event.py b/astrbot/core/platform/sources/gewechat/gewechat_event.py
index 15f0badd7..3aca64bab 100644
--- a/astrbot/core/platform/sources/gewechat/gewechat_event.py
+++ b/astrbot/core/platform/sources/gewechat/gewechat_event.py
@@ -6,7 +6,7 @@ from astrbot.core.utils.io import save_temp_img, download_file
from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk
from astrbot.api import logger
from astrbot.api.event import AstrMessageEvent, MessageChain
-from astrbot.api.platform import AstrBotMessage, PlatformMetadata
+from astrbot.api.platform import AstrBotMessage, PlatformMetadata, Group, MessageMember
from astrbot.api.message_components import Plain, Image, Record, At, File
from .client import SimpleGewechatClient
@@ -123,3 +123,30 @@ class GewechatPlatformEvent(AstrMessageEvent):
to_wxid = self.message_obj.raw_message.get("to_wxid", None)
await GewechatPlatformEvent.send_with_client(message, to_wxid, self.client)
await super().send(message)
+
+ async def get_group(self, group_id=None, **kwargs):
+ # 确定有效的 group_id
+ if group_id is None:
+ group_id = self.get_group_id()
+
+ if not group_id:
+ return None
+
+ res = await self.client.get_group(group_id)
+ data: dict = res["data"]
+
+ if not data["chatroomId"]:
+ return None
+
+ members = [
+ MessageMember(user_id=member["wxid"], nickname=member["nickName"])
+ for member in data.get("memberList", [])
+ ]
+
+ return Group(
+ group_id=data["chatroomId"],
+ group_name=data.get("nickName"),
+ group_avatar=data.get("smallHeadImgUrl"),
+ group_owner=data.get("chatRoomOwner"),
+ members=members,
+ )
diff --git a/compose.yml b/compose.yml
index 805d30c11..3bab93fc3 100644
--- a/compose.yml
+++ b/compose.yml
@@ -1,16 +1,21 @@
version: '3.8'
+# 当接入 QQ NapCat 时,请使用这个 compose 文件一件部署: https://github.com/NapNeko/NapCat-Docker/blob/main/compose/astrbot.yml
+
services:
astrbot:
image: soulter/astrbot:latest
container_name: astrbot
+ restart: always
ports: # mappings description: https://github.com/Soulter/AstrBot/issues/497
- - "6185:6185"
- - "6195:6195" # optional, wecom default port
- - "6199:6199" # optional, aiocqhttp default port
- - "6196:6196" # optional, qq official webhook default port
- - "11451:11451" # optional, gewechat default port
+ - "6185:6185" # 必选,AstrBot WebUI 端口
+ - "6195:6195" # 可选, 企业微信 Webhook 端口
+ - "6199:6199" # 可选, QQ 个人号 WebSocket 端口
+ - "6196:6196" # 可选, QQ 官方接口 Webhook 端口
+ - "11451:11451" # 可选, 微信个人号 Webhook 端口
+ environment:
+ - TZ=Asia/Shanghai
volumes:
- ./data:/AstrBot/data
- - /etc/timezone:/etc/timezone:ro
- - /etc/localtime:/etc/localtime:ro
+ # - /etc/timezone:/etc/timezone:ro
+ # - /etc/localtime:/etc/localtime:ro