AstrBot 4.8.0__py3-none-any.whl → 4.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- astrbot/cli/__init__.py +1 -1
- astrbot/core/agent/runners/tool_loop_agent_runner.py +0 -1
- astrbot/core/agent/tool.py +7 -2
- astrbot/core/astr_agent_tool_exec.py +5 -1
- astrbot/core/config/astrbot_config.py +4 -0
- astrbot/core/config/default.py +72 -1
- astrbot/core/config/i18n_utils.py +1 -0
- astrbot/core/core_lifecycle.py +1 -1
- astrbot/core/db/__init__.py +2 -3
- astrbot/core/db/migration/migra_3_to_4.py +2 -0
- astrbot/core/db/migration/sqlite_v3.py +6 -4
- astrbot/core/db/po.py +16 -15
- astrbot/core/db/sqlite.py +4 -3
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +2 -0
- astrbot/core/event_bus.py +6 -1
- astrbot/core/knowledge_base/retrieval/manager.py +5 -1
- astrbot/core/log.py +2 -1
- astrbot/core/message/components.py +9 -3
- astrbot/core/persona_mgr.py +2 -2
- astrbot/core/pipeline/content_safety_check/stage.py +1 -1
- astrbot/core/pipeline/context_utils.py +2 -1
- astrbot/core/pipeline/process_stage/method/star_request.py +1 -2
- astrbot/core/pipeline/process_stage/stage.py +1 -1
- astrbot/core/pipeline/respond/stage.py +8 -2
- astrbot/core/pipeline/result_decorate/stage.py +89 -22
- astrbot/core/pipeline/scheduler.py +5 -1
- astrbot/core/pipeline/waking_check/stage.py +10 -0
- astrbot/core/platform/astr_message_event.py +5 -3
- astrbot/core/platform/astrbot_message.py +2 -2
- astrbot/core/platform/manager.py +4 -0
- astrbot/core/platform/platform.py +11 -3
- astrbot/core/platform/platform_metadata.py +1 -1
- astrbot/core/platform/register.py +1 -0
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +8 -6
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +9 -5
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +24 -16
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +5 -2
- astrbot/core/platform/sources/discord/client.py +16 -4
- astrbot/core/platform/sources/discord/components.py +2 -2
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +52 -24
- astrbot/core/platform/sources/discord/discord_platform_event.py +29 -8
- astrbot/core/platform/sources/lark/lark_adapter.py +183 -20
- astrbot/core/platform/sources/lark/lark_event.py +39 -4
- astrbot/core/platform/sources/lark/server.py +206 -0
- astrbot/core/platform/sources/misskey/misskey_adapter.py +2 -3
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +62 -18
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +13 -7
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +5 -3
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +2 -1
- astrbot/core/platform/sources/slack/client.py +9 -2
- astrbot/core/platform/sources/slack/slack_adapter.py +15 -9
- astrbot/core/platform/sources/slack/slack_event.py +8 -7
- astrbot/core/platform/sources/telegram/tg_adapter.py +1 -1
- astrbot/core/platform/sources/telegram/tg_event.py +23 -27
- astrbot/core/platform/sources/webchat/webchat_adapter.py +2 -2
- astrbot/core/platform/sources/webchat/webchat_event.py +2 -2
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +26 -9
- astrbot/core/platform/sources/wecom/wecom_adapter.py +25 -28
- astrbot/core/platform/sources/wecom/wecom_event.py +2 -2
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +3 -3
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +30 -25
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +10 -7
- astrbot/core/provider/func_tool_manager.py +3 -3
- astrbot/core/provider/manager.py +130 -74
- astrbot/core/provider/provider.py +12 -1
- astrbot/core/provider/sources/azure_tts_source.py +31 -9
- astrbot/core/provider/sources/bailian_rerank_source.py +4 -0
- astrbot/core/provider/sources/dashscope_tts.py +3 -2
- astrbot/core/provider/sources/edge_tts_source.py +1 -1
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +5 -4
- astrbot/core/provider/sources/gemini_embedding_source.py +15 -5
- astrbot/core/provider/sources/gemini_source.py +12 -10
- astrbot/core/provider/sources/minimax_tts_api_source.py +4 -2
- astrbot/core/provider/sources/openai_embedding_source.py +2 -2
- astrbot/core/provider/sources/openai_source.py +4 -0
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +5 -2
- astrbot/core/provider/sources/vllm_rerank_source.py +1 -0
- astrbot/core/provider/sources/whisper_api_source.py +1 -1
- astrbot/core/provider/sources/whisper_selfhosted_source.py +6 -2
- astrbot/core/provider/sources/xinference_rerank_source.py +10 -2
- astrbot/core/star/context.py +2 -2
- astrbot/core/star/register/star_handler.py +22 -5
- astrbot/core/star/star_handler.py +85 -4
- astrbot/core/updator.py +3 -3
- astrbot/core/utils/io.py +1 -1
- astrbot/core/utils/session_waiter.py +17 -10
- astrbot/core/utils/shared_preferences.py +32 -0
- astrbot/core/utils/t2i/__init__.py +2 -2
- astrbot/core/utils/t2i/local_strategy.py +25 -31
- astrbot/core/utils/tencent_record_helper.py +1 -1
- astrbot/core/utils/version_comparator.py +6 -3
- astrbot/core/utils/webhook_utils.py +19 -0
- astrbot/dashboard/routes/chat.py +14 -9
- astrbot/dashboard/routes/config.py +10 -20
- astrbot/dashboard/routes/conversation.py +91 -1
- astrbot/dashboard/routes/knowledge_base.py +253 -78
- astrbot/dashboard/routes/log.py +13 -8
- astrbot/dashboard/routes/platform.py +1 -1
- astrbot/dashboard/routes/plugin.py +113 -52
- astrbot/dashboard/routes/route.py +2 -0
- astrbot/dashboard/server.py +6 -3
- {astrbot-4.8.0.dist-info → astrbot-4.9.1.dist-info}/METADATA +9 -1
- {astrbot-4.8.0.dist-info → astrbot-4.9.1.dist-info}/RECORD +106 -105
- {astrbot-4.8.0.dist-info → astrbot-4.9.1.dist-info}/WHEEL +0 -0
- {astrbot-4.8.0.dist-info → astrbot-4.9.1.dist-info}/entry_points.txt +0 -0
- {astrbot-4.8.0.dist-info → astrbot-4.9.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import hashlib
|
|
2
3
|
import json
|
|
3
4
|
import os
|
|
4
5
|
import ssl
|
|
5
6
|
import traceback
|
|
7
|
+
from dataclasses import dataclass
|
|
6
8
|
from datetime import datetime
|
|
7
9
|
|
|
8
10
|
import aiohttp
|
|
9
11
|
import certifi
|
|
10
12
|
from quart import request
|
|
11
13
|
|
|
14
|
+
from astrbot.api import sp
|
|
12
15
|
from astrbot.core import DEMO_MODE, file_token_service, logger
|
|
13
16
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
14
17
|
from astrbot.core.star.filter.command import CommandFilter
|
|
@@ -25,6 +28,13 @@ PLUGIN_UPDATE_CONCURRENCY = (
|
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
|
|
31
|
+
@dataclass
|
|
32
|
+
class RegistrySource:
|
|
33
|
+
urls: list[str]
|
|
34
|
+
cache_file: str
|
|
35
|
+
md5_url: str | None # None means "no remote MD5, always treat cache as stale"
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
class PluginRoute(Route):
|
|
29
39
|
def __init__(
|
|
30
40
|
self,
|
|
@@ -45,6 +55,8 @@ class PluginRoute(Route):
|
|
|
45
55
|
"/plugin/on": ("POST", self.on_plugin),
|
|
46
56
|
"/plugin/reload": ("POST", self.reload_plugins),
|
|
47
57
|
"/plugin/readme": ("GET", self.get_plugin_readme),
|
|
58
|
+
"/plugin/source/get": ("GET", self.get_custom_source),
|
|
59
|
+
"/plugin/source/save": ("POST", self.save_custom_source),
|
|
48
60
|
}
|
|
49
61
|
self.core_lifecycle = core_lifecycle
|
|
50
62
|
self.plugin_manager = plugin_manager
|
|
@@ -84,22 +96,15 @@ class PluginRoute(Route):
|
|
|
84
96
|
custom = request.args.get("custom_registry")
|
|
85
97
|
force_refresh = request.args.get("force_refresh", "false").lower() == "true"
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if custom:
|
|
90
|
-
urls = [custom]
|
|
91
|
-
else:
|
|
92
|
-
urls = [
|
|
93
|
-
"https://api.soulter.top/astrbot/plugins",
|
|
94
|
-
"https://github.com/AstrBotDevs/AstrBot_Plugins_Collection/raw/refs/heads/main/plugin_cache_original.json",
|
|
95
|
-
]
|
|
99
|
+
# 构建注册表源信息
|
|
100
|
+
source = self._build_registry_source(custom)
|
|
96
101
|
|
|
97
102
|
# 如果不是强制刷新,先检查缓存是否有效
|
|
98
103
|
cached_data = None
|
|
99
104
|
if not force_refresh:
|
|
100
105
|
# 先检查MD5是否匹配,如果匹配则使用缓存
|
|
101
|
-
if await self._is_cache_valid(
|
|
102
|
-
cached_data = self._load_plugin_cache(cache_file)
|
|
106
|
+
if await self._is_cache_valid(source):
|
|
107
|
+
cached_data = self._load_plugin_cache(source.cache_file)
|
|
103
108
|
if cached_data:
|
|
104
109
|
logger.debug("缓存MD5匹配,使用缓存的插件市场数据")
|
|
105
110
|
return Response().ok(cached_data).__dict__
|
|
@@ -109,7 +114,7 @@ class PluginRoute(Route):
|
|
|
109
114
|
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
|
110
115
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
111
116
|
|
|
112
|
-
for url in urls:
|
|
117
|
+
for url in source.urls:
|
|
113
118
|
try:
|
|
114
119
|
async with (
|
|
115
120
|
aiohttp.ClientSession(
|
|
@@ -119,7 +124,11 @@ class PluginRoute(Route):
|
|
|
119
124
|
session.get(url) as response,
|
|
120
125
|
):
|
|
121
126
|
if response.status == 200:
|
|
122
|
-
|
|
127
|
+
try:
|
|
128
|
+
remote_data = await response.json()
|
|
129
|
+
except aiohttp.ContentTypeError:
|
|
130
|
+
remote_text = await response.text()
|
|
131
|
+
remote_data = json.loads(remote_text)
|
|
123
132
|
|
|
124
133
|
# 检查远程数据是否为空
|
|
125
134
|
if not remote_data or (
|
|
@@ -128,11 +137,13 @@ class PluginRoute(Route):
|
|
|
128
137
|
logger.warning(f"远程插件市场数据为空: {url}")
|
|
129
138
|
continue # 继续尝试其他URL或使用缓存
|
|
130
139
|
|
|
131
|
-
logger.info(
|
|
140
|
+
logger.info(
|
|
141
|
+
f"成功获取远程插件市场数据,包含 {len(remote_data)} 个插件"
|
|
142
|
+
)
|
|
132
143
|
# 获取最新的MD5并保存到缓存
|
|
133
|
-
current_md5 = await self.
|
|
144
|
+
current_md5 = await self._fetch_remote_md5(source.md5_url)
|
|
134
145
|
self._save_plugin_cache(
|
|
135
|
-
cache_file,
|
|
146
|
+
source.cache_file,
|
|
136
147
|
remote_data,
|
|
137
148
|
current_md5,
|
|
138
149
|
)
|
|
@@ -143,7 +154,7 @@ class PluginRoute(Route):
|
|
|
143
154
|
|
|
144
155
|
# 如果远程获取失败,尝试使用缓存数据
|
|
145
156
|
if not cached_data:
|
|
146
|
-
cached_data = self._load_plugin_cache(cache_file)
|
|
157
|
+
cached_data = self._load_plugin_cache(source.cache_file)
|
|
147
158
|
|
|
148
159
|
if cached_data:
|
|
149
160
|
logger.warning("远程插件市场数据获取失败,使用缓存数据")
|
|
@@ -151,39 +162,47 @@ class PluginRoute(Route):
|
|
|
151
162
|
|
|
152
163
|
return Response().error("获取插件列表失败,且没有可用的缓存数据").__dict__
|
|
153
164
|
|
|
154
|
-
|
|
155
|
-
"""
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
# 加载缓存文件
|
|
161
|
-
with open(cache_file, encoding="utf-8") as f:
|
|
162
|
-
cache_data = json.load(f)
|
|
165
|
+
def _build_registry_source(self, custom_url: str | None) -> RegistrySource:
|
|
166
|
+
"""构建注册表源信息"""
|
|
167
|
+
if custom_url:
|
|
168
|
+
# 对自定义URL生成一个安全的文件名
|
|
169
|
+
url_hash = hashlib.md5(custom_url.encode()).hexdigest()[:8]
|
|
170
|
+
cache_file = f"data/plugins_custom_{url_hash}.json"
|
|
163
171
|
|
|
164
|
-
|
|
165
|
-
if
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
# 更安全的后缀处理方式
|
|
173
|
+
if custom_url.endswith(".json"):
|
|
174
|
+
md5_url = custom_url[:-5] + "-md5.json"
|
|
175
|
+
else:
|
|
176
|
+
md5_url = custom_url + "-md5.json"
|
|
168
177
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
urls = [custom_url]
|
|
179
|
+
else:
|
|
180
|
+
cache_file = "data/plugins.json"
|
|
181
|
+
md5_url = "https://api.soulter.top/astrbot/plugins-md5"
|
|
182
|
+
urls = [
|
|
183
|
+
"https://api.soulter.top/astrbot/plugins",
|
|
184
|
+
"https://github.com/AstrBotDevs/AstrBot_Plugins_Collection/raw/refs/heads/main/plugin_cache_original.json",
|
|
185
|
+
]
|
|
186
|
+
return RegistrySource(urls=urls, cache_file=cache_file, md5_url=md5_url)
|
|
174
187
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return is_valid
|
|
188
|
+
def _load_cached_md5(self, cache_file: str) -> str | None:
|
|
189
|
+
"""从缓存文件中加载MD5"""
|
|
190
|
+
if not os.path.exists(cache_file):
|
|
191
|
+
return None
|
|
180
192
|
|
|
193
|
+
try:
|
|
194
|
+
with open(cache_file, encoding="utf-8") as f:
|
|
195
|
+
cache_data = json.load(f)
|
|
196
|
+
return cache_data.get("md5")
|
|
181
197
|
except Exception as e:
|
|
182
|
-
logger.warning(f"
|
|
183
|
-
return
|
|
198
|
+
logger.warning(f"加载缓存MD5失败: {e}")
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
async def _fetch_remote_md5(self, md5_url: str | None) -> str | None:
|
|
202
|
+
"""获取远程MD5"""
|
|
203
|
+
if not md5_url:
|
|
204
|
+
return None
|
|
184
205
|
|
|
185
|
-
async def _get_remote_md5(self) -> str:
|
|
186
|
-
"""获取远程插件数据的MD5"""
|
|
187
206
|
try:
|
|
188
207
|
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
|
189
208
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
@@ -193,18 +212,37 @@ class PluginRoute(Route):
|
|
|
193
212
|
trust_env=True,
|
|
194
213
|
connector=connector,
|
|
195
214
|
) as session,
|
|
196
|
-
session.get(
|
|
197
|
-
"https://api.soulter.top/astrbot/plugins-md5",
|
|
198
|
-
) as response,
|
|
215
|
+
session.get(md5_url) as response,
|
|
199
216
|
):
|
|
200
217
|
if response.status == 200:
|
|
201
218
|
data = await response.json()
|
|
202
219
|
return data.get("md5", "")
|
|
203
|
-
logger.error(f"获取MD5失败,状态码:{response.status}")
|
|
204
|
-
return ""
|
|
205
220
|
except Exception as e:
|
|
206
|
-
logger.
|
|
207
|
-
|
|
221
|
+
logger.debug(f"获取远程MD5失败: {e}")
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
async def _is_cache_valid(self, source: RegistrySource) -> bool:
|
|
225
|
+
"""检查缓存是否有效(基于MD5)"""
|
|
226
|
+
try:
|
|
227
|
+
cached_md5 = self._load_cached_md5(source.cache_file)
|
|
228
|
+
if not cached_md5:
|
|
229
|
+
logger.debug("缓存文件中没有MD5信息")
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
remote_md5 = await self._fetch_remote_md5(source.md5_url)
|
|
233
|
+
if remote_md5 is None:
|
|
234
|
+
logger.warning("无法获取远程MD5,将使用缓存")
|
|
235
|
+
return True # 如果无法获取远程MD5,认为缓存有效
|
|
236
|
+
|
|
237
|
+
is_valid = cached_md5 == remote_md5
|
|
238
|
+
logger.debug(
|
|
239
|
+
f"插件数据MD5: 本地={cached_md5}, 远程={remote_md5}, 有效={is_valid}",
|
|
240
|
+
)
|
|
241
|
+
return is_valid
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
logger.warning(f"检查缓存有效性失败: {e}")
|
|
245
|
+
return False
|
|
208
246
|
|
|
209
247
|
def _load_plugin_cache(self, cache_file: str):
|
|
210
248
|
"""加载本地缓存的插件市场数据"""
|
|
@@ -545,9 +583,13 @@ class PluginRoute(Route):
|
|
|
545
583
|
logger.warning(f"插件 {plugin_name} 不存在")
|
|
546
584
|
return Response().error(f"插件 {plugin_name} 不存在").__dict__
|
|
547
585
|
|
|
586
|
+
if not plugin_obj.root_dir_name:
|
|
587
|
+
logger.warning(f"插件 {plugin_name} 目录不存在")
|
|
588
|
+
return Response().error(f"插件 {plugin_name} 目录不存在").__dict__
|
|
589
|
+
|
|
548
590
|
plugin_dir = os.path.join(
|
|
549
591
|
self.plugin_manager.plugin_store_path,
|
|
550
|
-
plugin_obj.root_dir_name,
|
|
592
|
+
plugin_obj.root_dir_name or "",
|
|
551
593
|
)
|
|
552
594
|
|
|
553
595
|
if not os.path.isdir(plugin_dir):
|
|
@@ -572,3 +614,22 @@ class PluginRoute(Route):
|
|
|
572
614
|
except Exception as e:
|
|
573
615
|
logger.error(f"/api/plugin/readme: {traceback.format_exc()}")
|
|
574
616
|
return Response().error(f"读取README文件失败: {e!s}").__dict__
|
|
617
|
+
|
|
618
|
+
async def get_custom_source(self):
|
|
619
|
+
"""获取自定义插件源"""
|
|
620
|
+
sources = await sp.global_get("custom_plugin_sources", [])
|
|
621
|
+
return Response().ok(sources).__dict__
|
|
622
|
+
|
|
623
|
+
async def save_custom_source(self):
|
|
624
|
+
"""保存自定义插件源"""
|
|
625
|
+
try:
|
|
626
|
+
data = await request.get_json()
|
|
627
|
+
sources = data.get("sources", [])
|
|
628
|
+
if not isinstance(sources, list):
|
|
629
|
+
return Response().error("sources fields must be a list").__dict__
|
|
630
|
+
|
|
631
|
+
await sp.global_put("custom_plugin_sources", sources)
|
|
632
|
+
return Response().ok(None, "保存成功").__dict__
|
|
633
|
+
except Exception as e:
|
|
634
|
+
logger.error(f"/api/plugin/source/save: {traceback.format_exc()}")
|
|
635
|
+
return Response().error(str(e)).__dict__
|
astrbot/dashboard/server.py
CHANGED
|
@@ -2,9 +2,12 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
import socket
|
|
5
|
+
from typing import cast
|
|
5
6
|
|
|
6
7
|
import jwt
|
|
7
8
|
import psutil
|
|
9
|
+
from flask.json.provider import DefaultJSONProvider
|
|
10
|
+
from psutil._common import addr as psutil_addr
|
|
8
11
|
from quart import Quart, g, jsonify, request
|
|
9
12
|
from quart.logging import default_handler
|
|
10
13
|
|
|
@@ -21,7 +24,7 @@ from .routes.route import Response, RouteContext
|
|
|
21
24
|
from .routes.session_management import SessionManagementRoute
|
|
22
25
|
from .routes.t2i import T2iRoute
|
|
23
26
|
|
|
24
|
-
APP: Quart
|
|
27
|
+
APP: Quart
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class AstrBotDashboard:
|
|
@@ -48,7 +51,7 @@ class AstrBotDashboard:
|
|
|
48
51
|
self.app.config["MAX_CONTENT_LENGTH"] = (
|
|
49
52
|
128 * 1024 * 1024
|
|
50
53
|
) # 将 Flask 允许的最大上传文件体大小设置为 128 MB
|
|
51
|
-
self.app.json.sort_keys = False
|
|
54
|
+
cast(DefaultJSONProvider, self.app.json).sort_keys = False
|
|
52
55
|
self.app.before_request(self.auth_middleware)
|
|
53
56
|
# token 用于验证请求
|
|
54
57
|
logging.getLogger(self.app.name).removeHandler(default_handler)
|
|
@@ -147,7 +150,7 @@ class AstrBotDashboard:
|
|
|
147
150
|
"""获取占用端口的进程详细信息"""
|
|
148
151
|
try:
|
|
149
152
|
for conn in psutil.net_connections(kind="inet"):
|
|
150
|
-
if conn.laddr.port == port:
|
|
153
|
+
if cast(psutil_addr, conn.laddr).port == port:
|
|
151
154
|
try:
|
|
152
155
|
process = psutil.Process(conn.pid)
|
|
153
156
|
# 获取详细信息
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AstrBot
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.9.1
|
|
4
4
|
Summary: Easy-to-use multi-platform LLM chatbot and development framework
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: Astrbot,Astrbot Module,Astrbot Plugin
|
|
@@ -81,6 +81,7 @@ Description-Content-Type: text/markdown
|
|
|
81
81
|
<img src="https://img.shields.io/github/v/release/AstrBotDevs/AstrBot?color=76bad9" href="https://github.com/AstrBotDevs/AstrBot/releases/latest">
|
|
82
82
|
<img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="python">
|
|
83
83
|
<img src="https://deepwiki.com/badge.svg" href="https://deepwiki.com/AstrBotDevs/AstrBot">
|
|
84
|
+
<a href="https://zread.ai/AstrBotDevs/AstrBot" target="_blank"><img src="https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff" alt="zread"/></a>
|
|
84
85
|
<a href="https://hub.docker.com/r/soulter/astrbot"><img alt="Docker pull" src="https://img.shields.io/docker/pulls/soulter/astrbot.svg?color=76bad9"/></a>
|
|
85
86
|
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.soulter.top%2Fastrbot%2Fplugin-num&query=%24.result&suffix=%E4%B8%AA&label=%E6%8F%92%E4%BB%B6%E5%B8%82%E5%9C%BA&cacheSeconds=3600">
|
|
86
87
|
<img src="https://gitcode.com/Soulter/AstrBot/star/badge.svg" href="https://gitcode.com/Soulter/AstrBot">
|
|
@@ -267,6 +268,7 @@ pre-commit install
|
|
|
267
268
|
- 3 群:630166526
|
|
268
269
|
- 5 群:822130018
|
|
269
270
|
- 6 群:753075035
|
|
271
|
+
- 7 群:743746109
|
|
270
272
|
- 开发者群:975206796
|
|
271
273
|
|
|
272
274
|
### Telegram 群组
|
|
@@ -302,4 +304,10 @@ pre-commit install
|
|
|
302
304
|
|
|
303
305
|
</details>
|
|
304
306
|
|
|
307
|
+
<div align="center">
|
|
308
|
+
|
|
305
309
|
_私は、高性能ですから!_
|
|
310
|
+
|
|
311
|
+
<img src="https://files.astrbot.app/watashiwa-koseino-desukara.gif" width="100"/>
|
|
312
|
+
</div
|
|
313
|
+
|