butterbot-python 3.1.0.dev1__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 (79) hide show
  1. butterbot/__init__.py +15 -0
  2. butterbot/app/__init__.py +41 -0
  3. butterbot/app/bot_app.py +374 -0
  4. butterbot/app/config.py +156 -0
  5. butterbot/app/source_manager.py +367 -0
  6. butterbot/core/__init__.py +46 -0
  7. butterbot/core/api/__init__.py +9 -0
  8. butterbot/core/api/base_api.py +32 -0
  9. butterbot/core/context/README.md +1 -0
  10. butterbot/core/context/__init__.py +9 -0
  11. butterbot/core/context/api_registry.py +109 -0
  12. butterbot/core/context/app_context.py +51 -0
  13. butterbot/core/context/config_provider.py +14 -0
  14. butterbot/core/data/__init__.py +15 -0
  15. butterbot/core/data/base_data.py +29 -0
  16. butterbot/core/data/base_model.py +197 -0
  17. butterbot/core/event/README.md +1 -0
  18. butterbot/core/event/__init__.py +10 -0
  19. butterbot/core/event/event.py +27 -0
  20. butterbot/core/event/event_bus.py +276 -0
  21. butterbot/core/event/subscriber.py +130 -0
  22. butterbot/core/exceptions.py +90 -0
  23. butterbot/core/filter/__init__.py +11 -0
  24. butterbot/core/filter/base_filter.py +74 -0
  25. butterbot/core/source/README.md +1 -0
  26. butterbot/core/source/__init__.py +9 -0
  27. butterbot/core/source/base_source.py +134 -0
  28. butterbot/core/types/__init__.py +9 -0
  29. butterbot/core/types/base_type.py +74 -0
  30. butterbot/sources/__init__.py +1 -0
  31. butterbot/sources/bilibili/README.md +10 -0
  32. butterbot/sources/bilibili/__init__.py +20 -0
  33. butterbot/sources/bilibili/api/__init__.py +5 -0
  34. butterbot/sources/bilibili/api/bili_api.py +123 -0
  35. butterbot/sources/bilibili/data/__init__.py +52 -0
  36. butterbot/sources/bilibili/data/danmaku_gift_data.py +135 -0
  37. butterbot/sources/bilibili/data/danmaku_guard_data.py +54 -0
  38. butterbot/sources/bilibili/data/danmaku_msg_data.py +71 -0
  39. butterbot/sources/bilibili/data/dto/__init__.py +59 -0
  40. butterbot/sources/bilibili/data/dto/danmaku_gift_dto.py +193 -0
  41. butterbot/sources/bilibili/data/dto/danmaku_guard_buy_dto.py +54 -0
  42. butterbot/sources/bilibili/data/dto/danmaku_msg_dto.py +123 -0
  43. butterbot/sources/bilibili/data/dto/dynamic_dto.py +276 -0
  44. butterbot/sources/bilibili/data/dto/live_room_dto.py +169 -0
  45. butterbot/sources/bilibili/data/dto/video_part_dto.py +18 -0
  46. butterbot/sources/bilibili/data/dynamic_data.py +362 -0
  47. butterbot/sources/bilibili/data/live_room_data.py +162 -0
  48. butterbot/sources/bilibili/data/video_part.py +46 -0
  49. butterbot/sources/bilibili/source/__init__.py +15 -0
  50. butterbot/sources/bilibili/source/base_polling_source.py +130 -0
  51. butterbot/sources/bilibili/source/bili_danmaku_source.py +230 -0
  52. butterbot/sources/bilibili/source/bili_dynamic_source.py +135 -0
  53. butterbot/sources/bilibili/source/bili_live_source.py +137 -0
  54. butterbot/sources/bilibili/types/__init__.py +11 -0
  55. butterbot/sources/bilibili/types/bili_type.py +32 -0
  56. butterbot/sources/napcat/README.md +10 -0
  57. butterbot/sources/napcat/__init__.py +20 -0
  58. butterbot/sources/napcat/api/__init__.py +3 -0
  59. butterbot/sources/napcat/api/napcat_api.py +316 -0
  60. butterbot/sources/napcat/data/__init__.py +179 -0
  61. butterbot/sources/napcat/data/event_data.py +441 -0
  62. butterbot/sources/napcat/data/segment_data.py +432 -0
  63. butterbot/sources/napcat/events.py +91 -0
  64. butterbot/sources/napcat/filters/__init__.py +19 -0
  65. butterbot/sources/napcat/filters/filters.py +202 -0
  66. butterbot/sources/napcat/source/__init__.py +5 -0
  67. butterbot/sources/napcat/source/napcat_source.py +58 -0
  68. butterbot/sources/napcat/types/__init__.py +5 -0
  69. butterbot/sources/napcat/types/napcat_type.py +61 -0
  70. butterbot/utils/README.md +15 -0
  71. butterbot/utils/__init__.py +11 -0
  72. butterbot/utils/data_pair.py +27 -0
  73. butterbot/utils/logging_config.py +521 -0
  74. butterbot/utils/terminal.py +308 -0
  75. butterbot/utils/websocket.py +1270 -0
  76. butterbot_python-3.1.0.dev1.dist-info/METADATA +769 -0
  77. butterbot_python-3.1.0.dev1.dist-info/RECORD +79 -0
  78. butterbot_python-3.1.0.dev1.dist-info/WHEEL +4 -0
  79. butterbot_python-3.1.0.dev1.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,308 @@
1
+ # -------------------------
2
+ # @Author : Fish-LP fish.zh@outlook.com
3
+ # @Date : 2025-06-22 17:03:21
4
+ # @LastEditors : Fish-LP fish.zh@outlook.com
5
+ # @LastEditTime : 2025-06-22 19:15:54
6
+ # @Description : 终端颜色
7
+ # @Copyright (c) 2025 by Fish.zh@outlook.com, MIT 使用许可协议
8
+ # -------------------------
9
+
10
+ import ctypes
11
+ import sys
12
+
13
+ if sys.platform.startswith("win"):
14
+ from ctypes import wintypes
15
+
16
+
17
+ def is_ansi_supported() -> bool:
18
+ """
19
+ 检查系统是否支持 ANSI 转义序列
20
+ """
21
+ if not sys.platform.startswith("win"):
22
+ # 非 Windows 系统通常支持 ANSI 转义序列
23
+ return True
24
+
25
+ # 检查 Windows 版本
26
+ is_windows_10_or_higher = False
27
+ try:
28
+ # 获取 Windows 版本信息
29
+ version_info = sys.getwindowsversion() # pyright: ignore[reportAttributeAccessIssue]
30
+ major_version = version_info[0]
31
+
32
+ # Windows 10 (major version 10) 或更高版本
33
+ if major_version >= 10:
34
+ is_windows_10_or_higher = True
35
+ except AttributeError:
36
+ # 如果无法获取版本信息,假设不支持
37
+ return False
38
+
39
+ # 检查控制台是否支持虚拟终端处理
40
+ kernel32 = ctypes.windll.kernel32 # pyright: ignore[reportAttributeAccessIssue]
41
+ stdout_handle = kernel32.GetStdHandle(-11)
42
+ if stdout_handle == wintypes.HANDLE(-1).value:
43
+ return False
44
+
45
+ # 获取当前控制台模式
46
+ console_mode = wintypes.DWORD()
47
+ if not kernel32.GetConsoleMode(stdout_handle, ctypes.byref(console_mode)):
48
+ return False
49
+
50
+ # 检查是否支持虚拟终端处理
51
+ return (console_mode.value & 0x0004) != 0 or is_windows_10_or_higher
52
+
53
+
54
+ def set_console_mode(mode: int = 7) -> bool:
55
+ """
56
+ 设置控制台输出模式
57
+ 尝试启用控制台的 ANSI 转义序列支持
58
+ """
59
+ if not sys.platform.startswith("win"):
60
+ return False
61
+ try:
62
+ kernel32 = ctypes.windll.kernel32 # pyright: ignore[reportAttributeAccessIssue]
63
+ # 获取标准输出句柄
64
+ stdout_handle = kernel32.GetStdHandle(-11)
65
+ if stdout_handle == wintypes.HANDLE(-1).value:
66
+ return False
67
+
68
+ # 设置控制台模式
69
+ if not kernel32.SetConsoleMode(stdout_handle, mode):
70
+ return False
71
+ except Exception:
72
+ return False
73
+ return True
74
+
75
+
76
+ class _ColorMeta(type):
77
+ """让颜色开关同时作用于类属性访问."""
78
+
79
+ def __getattribute__(cls, name: str):
80
+ value = super().__getattribute__(name)
81
+ if (
82
+ name != "_COLOR"
83
+ and name.isupper()
84
+ and isinstance(value, str)
85
+ and not super().__getattribute__("_COLOR")
86
+ ):
87
+ return ""
88
+ return value
89
+
90
+
91
+ class Color(metaclass=_ColorMeta):
92
+ """
93
+ 提供终端颜色和样式配置功能
94
+
95
+ 此类封装了 ANSI 转义序列,用于在支持的终端中显示颜色和样式
96
+ 支持前景色、背景色、样式设置以及 RGB 颜色和 256 色模式
97
+
98
+ Attributes:
99
+ _COLOR (bool): 表示是否启用颜色输出,默认为 True
100
+
101
+ Example:
102
+ >>> Color._COLOR = is_ansi_supported()
103
+ >>> print("%s%s%s" % (Color.RED, "红色文本", Color.RESET))
104
+ # 输出红色文本,后跟重置代码
105
+ """
106
+
107
+ _COLOR = is_ansi_supported() # 终端是否支持 ANSI 颜色
108
+
109
+ # 前景颜色
110
+ BLACK = "\033[30m"
111
+ """# 前景-黑"""
112
+ RED = "\033[31m"
113
+ """# 前景-红"""
114
+ GREEN = "\033[32m"
115
+ """# 前景-绿"""
116
+ YELLOW = "\033[33m"
117
+ """# 前景-黄"""
118
+ BLUE = "\033[34m"
119
+ """# 前景-蓝"""
120
+ MAGENTA = "\033[35m"
121
+ """# 前景-品红"""
122
+ CYAN = "\033[36m"
123
+ """# 前景-青"""
124
+ WHITE = "\033[37m"
125
+ """# 前景-白"""
126
+ GRAY = "\033[90m"
127
+ """# 前景-灰"""
128
+
129
+ # 背景颜色
130
+ BG_BLACK = "\033[40m"
131
+ """# 背景-黑"""
132
+ BG_RED = "\033[41m"
133
+ """# 背景-红"""
134
+ BG_GREEN = "\033[42m"
135
+ """# 背景-绿"""
136
+ BG_YELLOW = "\033[43m"
137
+ """# 背景-黄"""
138
+ BG_BLUE = "\033[44m"
139
+ """# 背景-蓝"""
140
+ BG_MAGENTA = "\033[45m"
141
+ """# 背景-品红"""
142
+ BG_CYAN = "\033[46m"
143
+ """# 背景-青"""
144
+ BG_WHITE = "\033[47m"
145
+ """# 背景-白"""
146
+ BG_GRAY = "\033[100m"
147
+ """# 背景-灰"""
148
+
149
+ # 样式
150
+ RESET = "\033[0m"
151
+ """# 重置所有颜色和样式"""
152
+ BOLD = "\033[1m"
153
+ """# 加粗"""
154
+ UNDERLINE = "\033[4m"
155
+ """# 下划线"""
156
+ REVERSE = "\033[7m"
157
+ """# 反转
158
+ - 前景色和背景色互换"""
159
+ ITALIC = "\033[3m"
160
+ """# 斜体"""
161
+ BLINK = "\033[5m"
162
+ """# 闪烁"""
163
+ STRIKE = "\033[9m"
164
+ """# 删除线"""
165
+
166
+ @classmethod
167
+ def from_rgb(cls, r: int, g: int, b: int, background: bool = False) -> str:
168
+ """
169
+ 从 RGB 颜色代码创建颜色代码
170
+
171
+ Args:
172
+ r (int): 红色分量 (0-255)
173
+ g (int): 绿色分量 (0-255)
174
+ b (int): 蓝色分量 (0-255)
175
+ background (bool): 是否是背景颜色,默认为前景颜色
176
+
177
+ Returns:
178
+ str: 对应的 ANSI 颜色代码,或空字符串(如果颜色输出被禁用)
179
+
180
+ Raises:
181
+ ValueError: 如果 r、g 或 b 的值超出范围 0-255
182
+
183
+ Example:
184
+ >>> rgb_color = Color.from_rgb(255, 0, 0)
185
+ >>> print("%s%s%s" % (rgb_color, "纯红文本", Color.RESET))
186
+ """
187
+ if not cls._COLOR:
188
+ return ""
189
+ if not (0 <= r <= 255 and 0 <= g <= 255 and 0 <= b <= 255):
190
+ raise ValueError("RGB 值超出范围 0-255")
191
+
192
+ if background:
193
+ return "\033[48;2;%s;%s;%sm" % (r, g, b)
194
+ else:
195
+ return "\033[38;2;%s;%s;%sm" % (r, g, b)
196
+
197
+ @classmethod
198
+ def rgb(cls, r: int, g: int, b: int) -> str:
199
+ """
200
+ 创建前景 RGB 颜色
201
+
202
+ Args:
203
+ r (int): 红色分量 (0-255)
204
+ g (int): 绿色分量 (0-255)
205
+ b (int): 蓝色分量 (0-255)
206
+
207
+ Returns:
208
+ str: 对应的前景 ANSI 颜色代码
209
+
210
+ Example:
211
+ >>> color = Color.rgb(0, 255, 0)
212
+ >>> print("%s%s%s" % (color, "绿色文本", Color.RESET))
213
+ """
214
+ return cls.from_rgb(r, g, b, background=False)
215
+
216
+ @classmethod
217
+ def bg_rgb(cls, r: int, g: int, b: int) -> str:
218
+ """
219
+ 创建背景 RGB 颜色
220
+
221
+ Args:
222
+ r (int): 红色分量 (0-255)
223
+ g (int): 绿色分量 (0-255)
224
+ b (int): 蓝色分量 (0-255)
225
+
226
+ Returns:
227
+ str: 对应的背景 ANSI 颜色代码
228
+
229
+ Example:
230
+ >>> bg_color = Color.bg_rgb(0, 0, 255)
231
+ >>> print("%s%s%s%s" % (bg_color, Color.WHITE, "蓝色背景文本", Color.RESET))
232
+ """
233
+ return cls.from_rgb(r, g, b, background=True)
234
+
235
+ @classmethod
236
+ def color256(cls, color_code: int, background: bool = False) -> str:
237
+ """
238
+ 使用 256 色模式创建颜色
239
+
240
+ Args:
241
+ color_code (int): 256 色中的颜色编号 (0-255)
242
+ background (bool): 是否是背景颜色,默认为前景颜色
243
+
244
+ Returns:
245
+ str: 对应的 ANSI 颜色代码,或空字符串(如果颜色输出被禁用)
246
+
247
+ Raises:
248
+ ValueError: 如果 color_code 的值超出范围 0-255
249
+
250
+ Example:
251
+ >>> color = Color.color256(196) # 256 色中的红色
252
+ >>> print("%s%s%s" % (color, "256 色文本", Color.RESET))
253
+ """
254
+ if not cls._COLOR:
255
+ return ""
256
+ if not 0 <= color_code <= 255:
257
+ raise ValueError("颜色代码超出范围 0-255")
258
+
259
+ if background:
260
+ return "\033[48;5;%sm" % color_code
261
+ else:
262
+ return "\033[38;5;%sm" % color_code
263
+
264
+ @classmethod
265
+ def rgb256(cls, r: int, g: int, b: int, background: bool = False) -> str:
266
+ """
267
+ 将 RGB 颜色转换为最接近的 256 色
268
+
269
+ 使用特定算法将 RGB 值映射到 256 色模式中的颜色编号
270
+
271
+ Args:
272
+ r (int): 红色分量 (0-255)
273
+ g (int): 绿色分量 (0-255)
274
+ b (int): 蓝色分量 (0-255)
275
+ background (bool): 是否是背景颜色,默认为前景颜色
276
+
277
+ Returns:
278
+ str: 对应的 ANSI 256 色代码,或空字符串(如果颜色输出被禁用)
279
+
280
+ Raises:
281
+ ValueError: 如果 r、g 或 b 的值超出范围 0-255
282
+
283
+ Example:
284
+ >>> color = Color.rgb256(128, 0, 128) # 紫色
285
+ >>> print("%s%s%s" % (color, "256 色近似紫色文本", Color.RESET))
286
+ """
287
+ if not cls._COLOR:
288
+ return ""
289
+ if not (0 <= r <= 255 and 0 <= g <= 255 and 0 <= b <= 255):
290
+ raise ValueError("RGB 值超出范围 0-255")
291
+
292
+ # 将 RGB 转换为 256 色
293
+ def rgb_to_256(r, g, b):
294
+ if r == g == b: # 灰度
295
+ if r < 8:
296
+ return 16
297
+ if r > 248:
298
+ return 231
299
+ return round((r - 8) / 247 * 24) + 232
300
+ return (
301
+ 16
302
+ + (36 * round(r / 255 * 5))
303
+ + (6 * round(g / 255 * 5))
304
+ + round(b / 255 * 5)
305
+ )
306
+
307
+ color_code = rgb_to_256(r, g, b)
308
+ return cls.color256(color_code, background)