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.
- butterbot/__init__.py +15 -0
- butterbot/app/__init__.py +41 -0
- butterbot/app/bot_app.py +374 -0
- butterbot/app/config.py +156 -0
- butterbot/app/source_manager.py +367 -0
- butterbot/core/__init__.py +46 -0
- butterbot/core/api/__init__.py +9 -0
- butterbot/core/api/base_api.py +32 -0
- butterbot/core/context/README.md +1 -0
- butterbot/core/context/__init__.py +9 -0
- butterbot/core/context/api_registry.py +109 -0
- butterbot/core/context/app_context.py +51 -0
- butterbot/core/context/config_provider.py +14 -0
- butterbot/core/data/__init__.py +15 -0
- butterbot/core/data/base_data.py +29 -0
- butterbot/core/data/base_model.py +197 -0
- butterbot/core/event/README.md +1 -0
- butterbot/core/event/__init__.py +10 -0
- butterbot/core/event/event.py +27 -0
- butterbot/core/event/event_bus.py +276 -0
- butterbot/core/event/subscriber.py +130 -0
- butterbot/core/exceptions.py +90 -0
- butterbot/core/filter/__init__.py +11 -0
- butterbot/core/filter/base_filter.py +74 -0
- butterbot/core/source/README.md +1 -0
- butterbot/core/source/__init__.py +9 -0
- butterbot/core/source/base_source.py +134 -0
- butterbot/core/types/__init__.py +9 -0
- butterbot/core/types/base_type.py +74 -0
- butterbot/sources/__init__.py +1 -0
- butterbot/sources/bilibili/README.md +10 -0
- butterbot/sources/bilibili/__init__.py +20 -0
- butterbot/sources/bilibili/api/__init__.py +5 -0
- butterbot/sources/bilibili/api/bili_api.py +123 -0
- butterbot/sources/bilibili/data/__init__.py +52 -0
- butterbot/sources/bilibili/data/danmaku_gift_data.py +135 -0
- butterbot/sources/bilibili/data/danmaku_guard_data.py +54 -0
- butterbot/sources/bilibili/data/danmaku_msg_data.py +71 -0
- butterbot/sources/bilibili/data/dto/__init__.py +59 -0
- butterbot/sources/bilibili/data/dto/danmaku_gift_dto.py +193 -0
- butterbot/sources/bilibili/data/dto/danmaku_guard_buy_dto.py +54 -0
- butterbot/sources/bilibili/data/dto/danmaku_msg_dto.py +123 -0
- butterbot/sources/bilibili/data/dto/dynamic_dto.py +276 -0
- butterbot/sources/bilibili/data/dto/live_room_dto.py +169 -0
- butterbot/sources/bilibili/data/dto/video_part_dto.py +18 -0
- butterbot/sources/bilibili/data/dynamic_data.py +362 -0
- butterbot/sources/bilibili/data/live_room_data.py +162 -0
- butterbot/sources/bilibili/data/video_part.py +46 -0
- butterbot/sources/bilibili/source/__init__.py +15 -0
- butterbot/sources/bilibili/source/base_polling_source.py +130 -0
- butterbot/sources/bilibili/source/bili_danmaku_source.py +230 -0
- butterbot/sources/bilibili/source/bili_dynamic_source.py +135 -0
- butterbot/sources/bilibili/source/bili_live_source.py +137 -0
- butterbot/sources/bilibili/types/__init__.py +11 -0
- butterbot/sources/bilibili/types/bili_type.py +32 -0
- butterbot/sources/napcat/README.md +10 -0
- butterbot/sources/napcat/__init__.py +20 -0
- butterbot/sources/napcat/api/__init__.py +3 -0
- butterbot/sources/napcat/api/napcat_api.py +316 -0
- butterbot/sources/napcat/data/__init__.py +179 -0
- butterbot/sources/napcat/data/event_data.py +441 -0
- butterbot/sources/napcat/data/segment_data.py +432 -0
- butterbot/sources/napcat/events.py +91 -0
- butterbot/sources/napcat/filters/__init__.py +19 -0
- butterbot/sources/napcat/filters/filters.py +202 -0
- butterbot/sources/napcat/source/__init__.py +5 -0
- butterbot/sources/napcat/source/napcat_source.py +58 -0
- butterbot/sources/napcat/types/__init__.py +5 -0
- butterbot/sources/napcat/types/napcat_type.py +61 -0
- butterbot/utils/README.md +15 -0
- butterbot/utils/__init__.py +11 -0
- butterbot/utils/data_pair.py +27 -0
- butterbot/utils/logging_config.py +521 -0
- butterbot/utils/terminal.py +308 -0
- butterbot/utils/websocket.py +1270 -0
- butterbot_python-3.1.0.dev1.dist-info/METADATA +769 -0
- butterbot_python-3.1.0.dev1.dist-info/RECORD +79 -0
- butterbot_python-3.1.0.dev1.dist-info/WHEEL +4 -0
- 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)
|