AstrBot 4.7.4__py3-none-any.whl → 4.9.0__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.
Files changed (111) hide show
  1. astrbot/cli/__init__.py +1 -1
  2. astrbot/core/agent/runners/tool_loop_agent_runner.py +0 -1
  3. astrbot/core/agent/tool.py +7 -2
  4. astrbot/core/astr_agent_run_util.py +15 -1
  5. astrbot/core/astr_agent_tool_exec.py +5 -1
  6. astrbot/core/config/astrbot_config.py +4 -0
  7. astrbot/core/config/default.py +116 -1
  8. astrbot/core/core_lifecycle.py +1 -1
  9. astrbot/core/db/__init__.py +32 -4
  10. astrbot/core/db/migration/migra_3_to_4.py +2 -0
  11. astrbot/core/db/migration/sqlite_v3.py +6 -4
  12. astrbot/core/db/po.py +16 -15
  13. astrbot/core/db/sqlite.py +56 -1
  14. astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +2 -0
  15. astrbot/core/event_bus.py +6 -1
  16. astrbot/core/knowledge_base/retrieval/manager.py +5 -1
  17. astrbot/core/log.py +2 -1
  18. astrbot/core/message/components.py +9 -3
  19. astrbot/core/persona_mgr.py +2 -2
  20. astrbot/core/pipeline/content_safety_check/stage.py +1 -1
  21. astrbot/core/pipeline/context_utils.py +2 -1
  22. astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +1 -1
  23. astrbot/core/pipeline/process_stage/method/star_request.py +1 -2
  24. astrbot/core/pipeline/process_stage/stage.py +1 -1
  25. astrbot/core/pipeline/respond/stage.py +4 -2
  26. astrbot/core/pipeline/result_decorate/stage.py +68 -21
  27. astrbot/core/pipeline/scheduler.py +5 -1
  28. astrbot/core/pipeline/waking_check/stage.py +10 -0
  29. astrbot/core/platform/astr_message_event.py +5 -3
  30. astrbot/core/platform/astrbot_message.py +2 -2
  31. astrbot/core/platform/manager.py +71 -9
  32. astrbot/core/platform/platform.py +109 -4
  33. astrbot/core/platform/platform_metadata.py +1 -1
  34. astrbot/core/platform/register.py +1 -0
  35. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +8 -6
  36. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +13 -8
  37. astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +28 -22
  38. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +5 -2
  39. astrbot/core/platform/sources/discord/client.py +16 -4
  40. astrbot/core/platform/sources/discord/components.py +2 -2
  41. astrbot/core/platform/sources/discord/discord_platform_adapter.py +53 -26
  42. astrbot/core/platform/sources/discord/discord_platform_event.py +29 -8
  43. astrbot/core/platform/sources/lark/lark_adapter.py +178 -22
  44. astrbot/core/platform/sources/lark/lark_event.py +39 -4
  45. astrbot/core/platform/sources/lark/server.py +206 -0
  46. astrbot/core/platform/sources/misskey/misskey_adapter.py +3 -5
  47. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +64 -18
  48. astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +14 -10
  49. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -11
  50. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +15 -2
  51. astrbot/core/platform/sources/satori/satori_adapter.py +1 -2
  52. astrbot/core/platform/sources/slack/client.py +58 -40
  53. astrbot/core/platform/sources/slack/slack_adapter.py +36 -16
  54. astrbot/core/platform/sources/slack/slack_event.py +11 -10
  55. astrbot/core/platform/sources/telegram/tg_adapter.py +2 -3
  56. astrbot/core/platform/sources/telegram/tg_event.py +23 -27
  57. astrbot/core/platform/sources/webchat/webchat_adapter.py +97 -31
  58. astrbot/core/platform/sources/webchat/webchat_event.py +35 -35
  59. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +27 -11
  60. astrbot/core/platform/sources/wecom/wecom_adapter.py +75 -36
  61. astrbot/core/platform/sources/wecom/wecom_event.py +3 -3
  62. astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +26 -9
  63. astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +3 -3
  64. astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +27 -5
  65. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +81 -35
  66. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +11 -8
  67. astrbot/core/platform_message_history_mgr.py +3 -3
  68. astrbot/core/provider/func_tool_manager.py +3 -3
  69. astrbot/core/provider/manager.py +130 -74
  70. astrbot/core/provider/provider.py +12 -1
  71. astrbot/core/provider/sources/azure_tts_source.py +31 -9
  72. astrbot/core/provider/sources/bailian_rerank_source.py +4 -0
  73. astrbot/core/provider/sources/dashscope_tts.py +3 -2
  74. astrbot/core/provider/sources/edge_tts_source.py +1 -1
  75. astrbot/core/provider/sources/fishaudio_tts_api_source.py +5 -4
  76. astrbot/core/provider/sources/gemini_embedding_source.py +15 -5
  77. astrbot/core/provider/sources/gemini_source.py +12 -10
  78. astrbot/core/provider/sources/minimax_tts_api_source.py +4 -2
  79. astrbot/core/provider/sources/openai_embedding_source.py +2 -2
  80. astrbot/core/provider/sources/openai_source.py +4 -0
  81. astrbot/core/provider/sources/sensevoice_selfhosted_source.py +5 -2
  82. astrbot/core/provider/sources/vllm_rerank_source.py +1 -0
  83. astrbot/core/provider/sources/whisper_api_source.py +44 -12
  84. astrbot/core/provider/sources/whisper_selfhosted_source.py +6 -2
  85. astrbot/core/provider/sources/xinference_rerank_source.py +10 -2
  86. astrbot/core/star/context.py +2 -2
  87. astrbot/core/star/register/star_handler.py +22 -5
  88. astrbot/core/star/star_handler.py +85 -4
  89. astrbot/core/updator.py +3 -3
  90. astrbot/core/utils/io.py +1 -1
  91. astrbot/core/utils/session_waiter.py +17 -10
  92. astrbot/core/utils/shared_preferences.py +32 -0
  93. astrbot/core/utils/t2i/__init__.py +2 -2
  94. astrbot/core/utils/t2i/local_strategy.py +25 -31
  95. astrbot/core/utils/tencent_record_helper.py +2 -2
  96. astrbot/core/utils/version_comparator.py +6 -3
  97. astrbot/core/utils/webhook_utils.py +66 -0
  98. astrbot/dashboard/routes/__init__.py +2 -0
  99. astrbot/dashboard/routes/chat.py +311 -76
  100. astrbot/dashboard/routes/config.py +14 -5
  101. astrbot/dashboard/routes/knowledge_base.py +254 -79
  102. astrbot/dashboard/routes/log.py +13 -8
  103. astrbot/dashboard/routes/platform.py +100 -0
  104. astrbot/dashboard/routes/plugin.py +108 -51
  105. astrbot/dashboard/routes/route.py +2 -0
  106. astrbot/dashboard/server.py +9 -4
  107. {astrbot-4.7.4.dist-info → astrbot-4.9.0.dist-info}/METADATA +50 -37
  108. {astrbot-4.7.4.dist-info → astrbot-4.9.0.dist-info}/RECORD +111 -108
  109. {astrbot-4.7.4.dist-info → astrbot-4.9.0.dist-info}/WHEEL +0 -0
  110. {astrbot-4.7.4.dist-info → astrbot-4.9.0.dist-info}/entry_points.txt +0 -0
  111. {astrbot-4.7.4.dist-info → astrbot-4.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -20,7 +20,7 @@ class FontManager:
20
20
  _font_cache = {}
21
21
 
22
22
  @classmethod
23
- def get_font(cls, size: int) -> ImageFont.FreeTypeFont:
23
+ def get_font(cls, size: int) -> ImageFont.FreeTypeFont|ImageFont.ImageFont:
24
24
  """获取指定大小的字体,优先从缓存获取"""
25
25
  if size in cls._font_cache:
26
26
  return cls._font_cache[size]
@@ -66,23 +66,17 @@ class TextMeasurer:
66
66
  """测量文本尺寸的工具类"""
67
67
 
68
68
  @staticmethod
69
- def get_text_size(text: str, font: ImageFont.FreeTypeFont) -> Tuple[int, int]:
69
+ def get_text_size(text: str, font: ImageFont.FreeTypeFont|ImageFont.ImageFont) -> tuple[int, int]:
70
70
  """获取文本的尺寸"""
71
- try:
72
- # PIL 9.0.0 以上版本
73
- return (
74
- font.getbbox(text)[2:]
75
- if hasattr(font, "getbbox")
76
- else font.getsize(text)
77
- )
78
- except Exception:
79
- # 兼容旧版本
80
- return font.getsize(text)
71
+
72
+ # 依赖库Pillow>=11.2.1,不再需要考虑<9.0.0
73
+ left, top, right, bottom = font.getbbox("Hello world")
74
+ return int(right - left), int(bottom - top)
81
75
 
82
76
  @staticmethod
83
77
  def split_text_to_fit_width(
84
- text: str, font: ImageFont.FreeTypeFont, max_width: int
85
- ) -> List[str]:
78
+ text: str, font: ImageFont.FreeTypeFont|ImageFont.ImageFont, max_width: int
79
+ ) -> list[str]:
86
80
  """将文本拆分为多行,确保每行不超过指定宽度"""
87
81
  lines = []
88
82
  if not text:
@@ -126,7 +120,7 @@ class MarkdownElement(ABC):
126
120
  def render(
127
121
  self,
128
122
  image: Image.Image,
129
- draw: ImageDraw.Draw,
123
+ draw: ImageDraw.ImageDraw,
130
124
  x: int,
131
125
  y: int,
132
126
  image_width: int,
@@ -152,7 +146,7 @@ class TextElement(MarkdownElement):
152
146
  def render(
153
147
  self,
154
148
  image: Image.Image,
155
- draw: ImageDraw.Draw,
149
+ draw: ImageDraw.ImageDraw,
156
150
  x: int,
157
151
  y: int,
158
152
  image_width: int,
@@ -186,7 +180,7 @@ class BoldTextElement(MarkdownElement):
186
180
  def render(
187
181
  self,
188
182
  image: Image.Image,
189
- draw: ImageDraw.Draw,
183
+ draw: ImageDraw.ImageDraw,
190
184
  x: int,
191
185
  y: int,
192
186
  image_width: int,
@@ -251,7 +245,7 @@ class ItalicTextElement(MarkdownElement):
251
245
  def render(
252
246
  self,
253
247
  image: Image.Image,
254
- draw: ImageDraw.Draw,
248
+ draw: ImageDraw.ImageDraw,
255
249
  x: int,
256
250
  y: int,
257
251
  image_width: int,
@@ -299,7 +293,7 @@ class ItalicTextElement(MarkdownElement):
299
293
  # 倾斜变换,使用仿射变换实现斜体效果
300
294
  # 变换矩阵: [1, 0.2, 0, 0, 1, 0]
301
295
  italic_img = text_img.transform(
302
- text_img.size, Image.AFFINE, (1, 0.2, 0, 0, 1, 0), Image.BICUBIC
296
+ text_img.size, Image.Transform.AFFINE, (1, 0.2, 0, 0, 1, 0), Image.Resampling.BICUBIC
303
297
  )
304
298
 
305
299
  # 粘贴到原图像
@@ -331,7 +325,7 @@ class UnderlineTextElement(MarkdownElement):
331
325
  def render(
332
326
  self,
333
327
  image: Image.Image,
334
- draw: ImageDraw.Draw,
328
+ draw: ImageDraw.ImageDraw,
335
329
  x: int,
336
330
  y: int,
337
331
  image_width: int,
@@ -371,7 +365,7 @@ class StrikethroughTextElement(MarkdownElement):
371
365
  def render(
372
366
  self,
373
367
  image: Image.Image,
374
- draw: ImageDraw.Draw,
368
+ draw: ImageDraw.ImageDraw,
375
369
  x: int,
376
370
  y: int,
377
371
  image_width: int,
@@ -422,7 +416,7 @@ class HeaderElement(MarkdownElement):
422
416
  def render(
423
417
  self,
424
418
  image: Image.Image,
425
- draw: ImageDraw.Draw,
419
+ draw: ImageDraw.ImageDraw,
426
420
  x: int,
427
421
  y: int,
428
422
  image_width: int,
@@ -458,7 +452,7 @@ class QuoteElement(MarkdownElement):
458
452
  def render(
459
453
  self,
460
454
  image: Image.Image,
461
- draw: ImageDraw.Draw,
455
+ draw: ImageDraw.ImageDraw,
462
456
  x: int,
463
457
  y: int,
464
458
  image_width: int,
@@ -502,7 +496,7 @@ class ListItemElement(MarkdownElement):
502
496
  def render(
503
497
  self,
504
498
  image: Image.Image,
505
- draw: ImageDraw.Draw,
499
+ draw: ImageDraw.ImageDraw,
506
500
  x: int,
507
501
  y: int,
508
502
  image_width: int,
@@ -532,7 +526,7 @@ class ListItemElement(MarkdownElement):
532
526
  class CodeBlockElement(MarkdownElement):
533
527
  """代码块元素"""
534
528
 
535
- def __init__(self, content: List[str]):
529
+ def __init__(self, content: list[str]):
536
530
  super().__init__("\n".join(content))
537
531
 
538
532
  def calculate_height(self, image_width: int, font_size: int) -> int:
@@ -552,7 +546,7 @@ class CodeBlockElement(MarkdownElement):
552
546
  def render(
553
547
  self,
554
548
  image: Image.Image,
555
- draw: ImageDraw.Draw,
549
+ draw: ImageDraw.ImageDraw,
556
550
  x: int,
557
551
  y: int,
558
552
  image_width: int,
@@ -595,7 +589,7 @@ class InlineCodeElement(MarkdownElement):
595
589
  def render(
596
590
  self,
597
591
  image: Image.Image,
598
- draw: ImageDraw.Draw,
592
+ draw: ImageDraw.ImageDraw,
599
593
  x: int,
600
594
  y: int,
601
595
  image_width: int,
@@ -667,7 +661,7 @@ class ImageElement(MarkdownElement):
667
661
  def render(
668
662
  self,
669
663
  image: Image.Image,
670
- draw: ImageDraw.Draw,
664
+ draw: ImageDraw.ImageDraw,
671
665
  x: int,
672
666
  y: int,
673
667
  image_width: int,
@@ -686,7 +680,7 @@ class ImageElement(MarkdownElement):
686
680
  if pasted_image.width > max_width:
687
681
  ratio = max_width / pasted_image.width
688
682
  new_size = (int(max_width), int(pasted_image.height * ratio))
689
- pasted_image = pasted_image.resize(new_size, Image.LANCZOS)
683
+ pasted_image = pasted_image.resize(new_size, Image.Resampling.LANCZOS)
690
684
 
691
685
  # 计算居中位置
692
686
  paste_x = x + (image_width - pasted_image.width) // 2 - 10
@@ -705,7 +699,7 @@ class MarkdownParser:
705
699
  """Markdown解析器,将文本解析为元素"""
706
700
 
707
701
  @staticmethod
708
- async def parse(text: str) -> List[MarkdownElement]:
702
+ async def parse(text: str) -> list[MarkdownElement]:
709
703
  elements = []
710
704
  lines = text.split("\n")
711
705
 
@@ -847,7 +841,7 @@ class MarkdownRenderer:
847
841
  self,
848
842
  font_size: int = 26,
849
843
  width: int = 800,
850
- bg_color: Tuple[int, int, int] = (255, 255, 255),
844
+ bg_color: tuple[int, int, int] = (255, 255, 255),
851
845
  ):
852
846
  self.font_size = font_size
853
847
  self.width = width
@@ -36,7 +36,7 @@ async def wav_to_tencent_silk(wav_path: str, output_path: str) -> int:
36
36
  import pilk
37
37
  except (ImportError, ModuleNotFoundError) as _:
38
38
  raise Exception(
39
- "pilk 模块未安装,请前往管理面板->控制台->安装pip库 安装 pilk 这个库",
39
+ "pilk 模块未安装,请前往管理面板->平台日志->安装pip库 安装 pilk 这个库",
40
40
  )
41
41
  # with wave.open(wav_path, 'rb') as wav:
42
42
  # wav_data = wav.readframes(wav.getnframes())
@@ -68,7 +68,7 @@ async def convert_to_pcm_wav(input_path: str, output_path: str) -> str:
68
68
  from pyffmpeg import FFmpeg
69
69
 
70
70
  ff = FFmpeg()
71
- ff.convert(input=input_path, output=output_path)
71
+ ff.convert(input_file=input_path, output_file=output_path)
72
72
  except Exception as e:
73
73
  logger.debug(f"pyffmpeg 转换失败: {e}, 尝试使用 ffmpeg 命令行进行转换")
74
74
 
@@ -60,9 +60,12 @@ class VersionComparator:
60
60
  return -1
61
61
  if isinstance(p1, str) and isinstance(p2, int):
62
62
  return 1
63
- if (isinstance(p1, int) and isinstance(p2, int)) or (
64
- isinstance(p1, str) and isinstance(p2, str)
65
- ):
63
+ if isinstance(p1, int) and isinstance(p2, int):
64
+ if p1 > p2:
65
+ return 1
66
+ if p1 < p2:
67
+ return -1
68
+ if isinstance(p1, str) and isinstance(p2, str):
66
69
  if p1 > p2:
67
70
  return 1
68
71
  if p1 < p2:
@@ -0,0 +1,66 @@
1
+ import uuid
2
+
3
+ from astrbot.core import astrbot_config, logger
4
+ from astrbot.core.config.default import WEBHOOK_SUPPORTED_PLATFORMS
5
+
6
+
7
+ def _get_callback_api_base() -> str:
8
+ try:
9
+ return astrbot_config.get("callback_api_base", "").rstrip("/")
10
+ except Exception as e:
11
+ logger.error(f"获取 callback_api_base 失败: {e!s}")
12
+ return ""
13
+
14
+
15
+ def _get_dashboard_port() -> int:
16
+ try:
17
+ return astrbot_config.get("dashboard", {}).get("port", 6185)
18
+ except Exception as e:
19
+ logger.error(f"获取 dashboard 端口失败: {e!s}")
20
+ return 6185
21
+
22
+
23
+ def log_webhook_info(platform_name: str, webhook_uuid: str):
24
+ """打印美观的 webhook 信息日志
25
+
26
+ Args:
27
+ platform_name: 平台名称
28
+ webhook_uuid: webhook 的 UUID
29
+ """
30
+
31
+ callback_base = _get_callback_api_base()
32
+
33
+ if not callback_base:
34
+ callback_base = "http(s)://<your-astrbot-domain>"
35
+
36
+ if not callback_base.startswith("http"):
37
+ callback_base = f"http(s)://{callback_base}"
38
+
39
+ callback_base = callback_base.rstrip("/")
40
+ webhook_url = f"{callback_base}/api/platform/webhook/{webhook_uuid}"
41
+
42
+ display_log = (
43
+ "\n====================\n"
44
+ f"🔗 机器人平台 {platform_name} 已启用统一 Webhook 模式\n"
45
+ f"📍 Webhook 回调地址: \n"
46
+ f" ➜ http://<your-ip>:{_get_dashboard_port()}/api/platform/webhook/{webhook_uuid}\n"
47
+ f" ➜ {webhook_url}\n"
48
+ "====================\n"
49
+ )
50
+ logger.info(display_log)
51
+
52
+
53
+ def ensure_platform_webhook_config(platform_cfg: dict) -> bool:
54
+ """为支持统一 webhook 的平台自动生成 webhook_uuid
55
+
56
+ Args:
57
+ platform_cfg (dict): 平台配置字典
58
+
59
+ Returns:
60
+ bool: 如果生成了 webhook_uuid 则返回 True,否则返回 False
61
+ """
62
+ pt = platform_cfg.get("type", "")
63
+ if pt in WEBHOOK_SUPPORTED_PLATFORMS and not platform_cfg.get("webhook_uuid"):
64
+ platform_cfg["webhook_uuid"] = uuid.uuid4().hex[:16]
65
+ return True
66
+ return False
@@ -6,6 +6,7 @@ from .file import FileRoute
6
6
  from .knowledge_base import KnowledgeBaseRoute
7
7
  from .log import LogRoute
8
8
  from .persona import PersonaRoute
9
+ from .platform import PlatformRoute
9
10
  from .plugin import PluginRoute
10
11
  from .session_management import SessionManagementRoute
11
12
  from .stat import StatRoute
@@ -22,6 +23,7 @@ __all__ = [
22
23
  "KnowledgeBaseRoute",
23
24
  "LogRoute",
24
25
  "PersonaRoute",
26
+ "PlatformRoute",
25
27
  "PluginRoute",
26
28
  "SessionManagementRoute",
27
29
  "StatRoute",