-
Notifications
You must be signed in to change notification settings - Fork 3
feat: 同步最新的通知模块 #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: 同步最新的通知模块 #19
Conversation
Walkthrough推送系统完全重构。移除旧的单体式 Push 类和配置模块,建立模块化的 PushChannel 抽象基类架构,新增 20+ 个具体推送通道实现(如 Bark、钉钉、Telegram 等),引入 PushService 协调器、新的 PushConfig 管理系统和 CurlGenerator 工具类。 Changes
Sequence Diagram(s)sequenceDiagram
participant App as 应用
participant PushService as PushService
participant Config as PushConfig
participant Channel as PushChannel<br/>(具体实现)
participant External as 外部服务<br/>(钉钉/Discord等)
Note over App,External: 初始化流程
App->>PushService: 创建实例
PushService->>PushService: init_push_channels()
PushService->>PushService: 注册所有通道实例
PushService->>Config: 生成通道配置字段
Note over App,External: 同步推送流程
App->>PushService: push(content, title, channel_id)
PushService->>Config: push_config (获取/缓存)
PushService->>PushService: get_channel_config(channel_id)
PushService->>Channel: validate_config(config)
alt 配置有效
PushService->>Channel: push(config, title, content, image)
Channel->>External: HTTP 请求
External-->>Channel: 响应
Channel-->>PushService: (success, message)
else 配置无效
Channel-->>PushService: (False, 错误信息)
end
PushService-->>App: (success, message)
Note over App,External: 异步推送流程
App->>PushService: push_async(content, title)
PushService->>PushService: 线程池执行推送
PushService->>Channel: push(...) [后台]
Channel->>External: HTTP 请求 [后台]
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 重点审核区域:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
🧹 Nitpick comments (10)
src/script_chainer/context/script_chainer_context.py (1)
20-32: 初始化逻辑正确,考虑使用 logging.exception使用非阻塞锁模式正确防止了死锁,PushService 集成也很合理。不过在第 30 行,可以考虑使用
log.exception('初始化出错')替代log.error('初始化出错', exc_info=True),这是更简洁的写法。可选的改进:
try: self.push_service.init_push_channels() - except Exception: - log.error('初始化出错', exc_info=True) + except Exception: + log.exception('初始化出错') finally: self._init_lock.release()src/one_dragon/base/push/channel/fake.py (1)
5-47: 占位符设计合理FakePushChannel 作为占位符明确表示不支持的推送方法,设计合理。不过建议考虑在类文档字符串中明确说明此通道的用途,避免开发者误用。
可选改进:在类定义后添加文档字符串说明用途:
class FakePushChannel(PushChannel): + """ + 占位符推送通道,用于标记不再维护的推送方法。 + 此通道的所有操作都会返回失败状态。 + """ def __init__(self):src/script_chainer/win_exe/script_runner.py (2)
229-237: 上下文初始化逻辑正确,建议使用 logging.exception基于上下文的推送服务集成设计合理,错误处理也较为完善。建议在第 236 行使用
log.exception替代log.error以简化代码。可选改进:
try: ctx = ScriptChainerContext() ctx.init() - except Exception as e: - log.error(f'初始化上下文实例失败: {e}') + except Exception as e: + log.exception(f'初始化上下文实例失败: {e}')
269-274: 资源清理逻辑合理,建议使用 logging.exception在 finally 块中清理资源的设计正确。建议在第 274 行使用
log.exception简化错误日志记录。可选改进:
if ctx is not None: try: ctx.after_app_shutdown() - except Exception as e: - log.error(f'清理资源失败: {e}') + except Exception as e: + log.exception(f'清理资源失败: {e}')src/one_dragon/base/push/channel/ai_botk.py (1)
96-98: 简化 JSON 数据发送当前实现手动序列化 JSON 并编码,requests 库可以通过
json参数自动处理。应用此差异简化代码:
- # 发送请求 - headers = {"Content-Type": "application/json"} - response = requests.post(url, data=json.dumps(data).encode("utf-8"), headers=headers, timeout=15) + # 发送请求 + response = requests.post(url, json=data, timeout=15)src/one_dragon/base/push/channel/server_chan.py (1)
90-93: 建议使用一致的空字符串检查方式当前使用
len(push_key) == 0检查空字符串。为与其他渠道保持一致并处理仅包含空格的情况,建议使用.strip()。- if len(push_key) == 0: + if not push_key.strip(): return False, "PUSH_KEY 不能为空"src/one_dragon/base/push/channel/push_deer.py (1)
89-94: 错误消息可能过于冗长Line 94 将完整的响应 JSON 对象转换为字符串返回给用户。这可能包含大量技术细节,建议仅提取关键错误信息。
if len(content_result) > 0: return True, "推送成功" else: - return False, f"推送失败:{response_json}" + error_msg = response_json.get("content", {}).get("error", "未知错误") + return False, f"推送失败:{error_msg}"src/one_dragon/base/push/channel/gotify.py (1)
109-112: 内部异常处理冗余Lines 109-112 的外层异常处理块捕获内层已处理的异常,这是冗余的。内层的
except Exception已经覆盖所有异常。考虑移除外层的异常处理:
- except Exception as e: - return False, f"gotify 推送系统异常: {str(e)}"src/one_dragon/base/push/channel/smtp.py (2)
146-153: 建议重构成功返回位置Line 146 的
return语句在try块内,而 Ruff 建议将其移至else块以提高代码清晰度。smtp_server.sendmail( email, email, message.as_bytes() ) - - return True, "SMTP邮件推送成功" - except Exception as e: log.error('SMTP邮件推送异常', exc_info=True) return False, f"SMTP邮件推送异常: {str(e)}" - + else: + return True, "SMTP邮件推送成功" finally: smtp_server.close()
198-205: 未使用的变量Line 199 解析得到的
host变量未被使用(仅用于验证端口)。可以使用下划线前缀表示这是一个占位变量。try: - host, port = server.split(":") + _host, port = server.split(":") port_int = int(port) if port_int < 1 or port_int > 65535: return False, "端口号必须在1-65535之间"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (34)
src/one_dragon/base/config/push_config.py(0 hunks)src/one_dragon/base/notify/push.py(0 hunks)src/one_dragon/base/notify/push_cards.py(0 hunks)src/one_dragon/base/push/channel/ai_botk.py(1 hunks)src/one_dragon/base/push/channel/bark.py(1 hunks)src/one_dragon/base/push/channel/chronocat.py(1 hunks)src/one_dragon/base/push/channel/dingding.py(1 hunks)src/one_dragon/base/push/channel/discord.py(1 hunks)src/one_dragon/base/push/channel/fake.py(1 hunks)src/one_dragon/base/push/channel/feishu.py(1 hunks)src/one_dragon/base/push/channel/gotify.py(1 hunks)src/one_dragon/base/push/channel/i_got.py(1 hunks)src/one_dragon/base/push/channel/ntfy.py(1 hunks)src/one_dragon/base/push/channel/one_bot.py(1 hunks)src/one_dragon/base/push/channel/push_deer.py(1 hunks)src/one_dragon/base/push/channel/push_me.py(1 hunks)src/one_dragon/base/push/channel/push_plus.py(1 hunks)src/one_dragon/base/push/channel/q_msg.py(1 hunks)src/one_dragon/base/push/channel/server_chan.py(1 hunks)src/one_dragon/base/push/channel/smtp.py(1 hunks)src/one_dragon/base/push/channel/synology_chat.py(1 hunks)src/one_dragon/base/push/channel/telegram.py(1 hunks)src/one_dragon/base/push/channel/we_plus_bot.py(1 hunks)src/one_dragon/base/push/channel/webhook.py(1 hunks)src/one_dragon/base/push/channel/work_weixin.py(1 hunks)src/one_dragon/base/push/channel/wx_pusher.py(1 hunks)src/one_dragon/base/push/curl_generator.py(1 hunks)src/one_dragon/base/push/push_channel.py(1 hunks)src/one_dragon/base/push/push_channel_config.py(1 hunks)src/one_dragon/base/push/push_config.py(1 hunks)src/one_dragon/base/push/push_email_services.py(1 hunks)src/one_dragon/base/push/push_service.py(1 hunks)src/script_chainer/context/script_chainer_context.py(4 hunks)src/script_chainer/win_exe/script_runner.py(3 hunks)
💤 Files with no reviewable changes (3)
- src/one_dragon/base/notify/push_cards.py
- src/one_dragon/base/config/push_config.py
- src/one_dragon/base/notify/push.py
🧰 Additional context used
🧬 Code graph analysis (28)
src/one_dragon/base/push/channel/telegram.py (2)
src/one_dragon/base/push/push_channel.py (3)
PushChannel(10-151)get_proxy(138-151)image_to_bytes(59-83)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/server_chan.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/synology_chat.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/discord.py (2)
src/one_dragon/base/push/push_channel.py (3)
PushChannel(10-151)get_proxy(138-151)image_to_bytes(59-83)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/dingding.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/ai_botk.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/wx_pusher.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/one_bot.py (2)
src/one_dragon/base/push/push_channel.py (2)
PushChannel(10-151)image_to_base64(121-136)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/fake.py (1)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)
src/one_dragon/base/push/channel/gotify.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/webhook.py (2)
src/one_dragon/base/push/push_channel.py (3)
PushChannel(10-151)get_proxy(138-151)image_to_bytes(59-83)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/script_chainer/context/script_chainer_context.py (1)
src/one_dragon/base/push/push_service.py (2)
PushService(42-255)init_push_channels(57-94)
src/one_dragon/base/push/channel/we_plus_bot.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/smtp.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/i_got.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/bark.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/push_service.py (3)
src/one_dragon/base/push/push_channel.py (2)
PushChannel(10-151)get_proxy(138-151)src/one_dragon/base/push/push_channel_config.py (1)
PushChannelConfigField(21-35)src/one_dragon/base/push/push_config.py (13)
PushConfig(17-166)PushProxy(11-14)generate_channel_fields(83-116)custom_push_title(39-40)custom_push_title(43-44)send_image(47-49)send_image(52-53)get_channel_config_value(118-136)proxy(56-57)proxy(60-61)is_personal_proxy(64-65)personal_proxy(68-73)personal_proxy(76-81)
src/one_dragon/base/push/channel/q_msg.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/chronocat.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/work_weixin.py (2)
src/one_dragon/base/push/push_channel.py (2)
PushChannel(10-151)image_to_bytes(59-83)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/script_chainer/win_exe/script_runner.py (4)
src/script_chainer/context/script_chainer_context.py (3)
ScriptChainerContext(15-103)init(23-32)after_app_shutdown(98-103)src/one_dragon/base/config/yaml_operator.py (1)
is_file_exists(97-102)src/one_dragon/base/push/push_service.py (1)
push_async(220-243)src/script_chainer/config/script_config.py (1)
script_display_name(58-59)
src/one_dragon/base/push/channel/push_plus.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/push_config.py (2)
src/one_dragon/base/push/push_channel_config.py (1)
PushChannelConfigField(21-35)src/one_dragon/base/config/yaml_operator.py (2)
get(77-78)update(80-87)
src/one_dragon/base/push/channel/feishu.py (2)
src/one_dragon/base/push/push_channel.py (2)
PushChannel(10-151)image_to_bytes(59-83)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/ntfy.py (2)
src/one_dragon/base/push/push_channel.py (2)
PushChannel(10-151)image_to_bytes(59-83)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/push_deer.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/channel/push_me.py (2)
src/one_dragon/base/push/push_channel.py (1)
PushChannel(10-151)src/one_dragon/base/push/push_channel_config.py (2)
PushChannelConfigField(21-35)FieldTypeEnum(12-17)
src/one_dragon/base/push/push_channel.py (4)
src/one_dragon/base/push/channel/discord.py (2)
push(55-140)validate_config(142-161)src/one_dragon/base/push/channel/ntfy.py (2)
push(78-170)validate_config(173-188)src/one_dragon/base/push/channel/one_bot.py (2)
push(53-156)validate_config(158-178)src/one_dragon/base/push/push_channel_config.py (1)
PushChannelConfigField(21-35)
🪛 Ruff (0.14.2)
src/one_dragon/base/push/channel/telegram.py
59-59: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
62-62: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
62-62: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
104-104: Local variable headers is assigned to but never used
Remove assignment to unused variable headers
(F841)
122-122: Use explicit conversion flag
Replace with conversion flag
(RUF010)
125-125: Do not catch blind exception: Exception
(BLE001)
126-126: Use explicit conversion flag
Replace with conversion flag
(RUF010)
130-130: Do not catch blind exception: Exception
(BLE001)
131-131: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/server_chan.py
32-32: Unused method argument: image
(ARG002)
33-33: Unused method argument: proxy_url
(ARG002)
39-39: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
42-42: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
42-42: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
75-75: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
77-77: Do not catch blind exception: Exception
(BLE001)
78-78: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/synology_chat.py
52-52: Unused method argument: image
(ARG002)
53-53: Unused method argument: proxy_url
(ARG002)
59-59: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
62-62: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
62-62: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
91-91: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
91-91: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
93-93: Do not catch blind exception: Exception
(BLE001)
95-95: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/push_channel_config.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
13-13: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
25-25: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
src/one_dragon/base/push/channel/discord.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
67-67: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
70-70: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
70-70: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
125-125: Use pop instead of key in dict followed by del dict[key]
Replace if statement with .pop(..., None)
(RUF051)
136-136: Consider moving this statement to an else block
(TRY300)
138-138: Do not catch blind exception: Exception
(BLE001)
140-140: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/dingding.py
62-62: Unused method argument: image
(ARG002)
63-63: Unused method argument: proxy_url
(ARG002)
69-69: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
72-72: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
72-72: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
102-102: Comment contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF003)
102-102: Comment contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF003)
124-124: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
126-126: Do not catch blind exception: Exception
(BLE001)
127-127: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/ai_botk.py
37-37: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
54-54: Unused method argument: image
(ARG002)
55-55: Unused method argument: proxy_url
(ARG002)
61-61: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
64-64: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
64-64: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
107-107: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
110-110: Use explicit conversion flag
Replace with conversion flag
(RUF010)
111-111: Do not catch blind exception: Exception
(BLE001)
112-112: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/wx_pusher.py
51-51: Unused method argument: image
(ARG002)
52-52: Unused method argument: proxy_url
(ARG002)
58-58: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
61-61: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
61-61: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
77-77: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
121-121: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
124-124: Use explicit conversion flag
Replace with conversion flag
(RUF010)
125-125: Do not catch blind exception: Exception
(BLE001)
126-126: Use explicit conversion flag
Replace with conversion flag
(RUF010)
148-148: Comment contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF003)
148-148: Comment contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF003)
src/one_dragon/base/push/channel/one_bot.py
59-59: Unused method argument: proxy_url
(ARG002)
65-65: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
68-68: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
68-68: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
116-116: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
121-121: Do not catch blind exception: Exception
(BLE001)
122-122: Use explicit conversion flag
Replace with conversion flag
(RUF010)
137-137: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
142-142: Do not catch blind exception: Exception
(BLE001)
143-143: Use explicit conversion flag
Replace with conversion flag
(RUF010)
155-155: Do not catch blind exception: Exception
(BLE001)
156-156: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/fake.py
12-12: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
18-18: Unused method argument: config
(ARG002)
19-19: Unused method argument: title
(ARG002)
20-20: Unused method argument: content
(ARG002)
21-21: Unused method argument: image
(ARG002)
22-22: Unused method argument: proxy_url
(ARG002)
38-38: Unused method argument: config
(ARG002)
src/one_dragon/base/push/channel/gotify.py
54-54: Unused method argument: proxy_url
(ARG002)
60-60: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
63-63: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
63-63: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
78-78: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
80-80: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
98-98: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
101-101: f-string without any placeholders
Remove extraneous f prefix
(F541)
101-101: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
106-106: Use explicit conversion flag
Replace with conversion flag
(RUF010)
109-109: Do not catch blind exception: Exception
(BLE001)
110-110: Use explicit conversion flag
Replace with conversion flag
(RUF010)
114-114: Do not catch blind exception: Exception
(BLE001)
115-115: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/webhook.py
83-83: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
86-86: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
86-86: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
121-121: Comment contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF003)
121-121: Comment contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF003)
126-126: Comment contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF003)
126-126: Comment contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF003)
134-134: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
134-134: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
140-140: Do not catch blind exception: Exception
(BLE001)
150-150: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
176-176: Consider moving this statement to an else block
(TRY300)
176-176: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
180-180: Use explicit conversion flag
Replace with conversion flag
(RUF010)
181-181: Do not catch blind exception: Exception
(BLE001)
183-183: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/script_chainer/context/script_chainer_context.py
29-29: Do not catch blind exception: Exception
(BLE001)
85-85: Avoid specifying long messages outside the exception class
(TRY003)
src/one_dragon/base/push/channel/we_plus_bot.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
58-58: Unused method argument: image
(ARG002)
59-59: Unused method argument: proxy_url
(ARG002)
65-65: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
68-68: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
68-68: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
111-111: Do not catch blind exception: Exception
(BLE001)
113-113: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
113-113: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/smtp.py
57-57: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
82-82: Unused method argument: image
(ARG002)
83-83: Unused method argument: proxy_url
(ARG002)
89-89: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
92-92: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
92-92: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
132-132: Comment contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF003)
132-132: Comment contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF003)
146-146: Consider moving this statement to an else block
(TRY300)
148-148: Do not catch blind exception: Exception
(BLE001)
150-150: Use explicit conversion flag
Replace with conversion flag
(RUF010)
155-155: Do not catch blind exception: Exception
(BLE001)
157-157: Use explicit conversion flag
Replace with conversion flag
(RUF010)
196-196: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
196-196: String contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF001)
196-196: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
196-196: String contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF001)
199-199: Unpacked variable host is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
src/one_dragon/base/push/channel/i_got.py
42-42: Unused method argument: image
(ARG002)
43-43: Unused method argument: proxy_url
(ARG002)
49-49: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
52-52: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
52-52: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
80-80: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
82-82: Do not catch blind exception: Exception
(BLE001)
84-84: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/bark.py
84-84: Unused method argument: image
(ARG002)
85-85: Unused method argument: proxy_url
(ARG002)
91-91: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
94-94: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
94-94: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
157-157: Do not catch blind exception: Exception
(BLE001)
158-158: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/push_service.py
114-114: Create your own exception
(TRY002)
src/one_dragon/base/push/channel/q_msg.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
50-50: Unused method argument: image
(ARG002)
51-51: Unused method argument: proxy_url
(ARG002)
57-57: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
60-60: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
60-60: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
77-77: Comment contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF003)
77-77: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
92-92: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
94-94: Do not catch blind exception: Exception
(BLE001)
96-96: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
96-96: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/chronocat.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
62-62: Unused method argument: image
(ARG002)
63-63: Unused method argument: proxy_url
(ARG002)
69-69: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
72-72: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
72-72: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
150-150: Do not catch blind exception: Exception
(BLE001)
152-152: Use explicit conversion flag
Replace with conversion flag
(RUF010)
182-182: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
224-224: Consider moving this statement to an else block
(TRY300)
225-225: Do not catch blind exception: Exception
(BLE001)
src/one_dragon/base/push/channel/work_weixin.py
53-53: Unused method argument: proxy_url
(ARG002)
59-59: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
62-62: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
62-62: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
97-97: Do not catch blind exception: Exception
(BLE001)
104-104: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
111-111: Do not catch blind exception: Exception
(BLE001)
130-130: Do not catch blind exception: Exception
(BLE001)
131-131: Use explicit conversion flag
Replace with conversion flag
(RUF010)
133-133: Undefined name MatLike
(F821)
157-157: Probable use of insecure hash functions in hashlib: md5
(S324)
169-169: Do not catch blind exception: Exception
(BLE001)
176-176: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
src/script_chainer/win_exe/script_runner.py
235-235: Do not catch blind exception: Exception
(BLE001)
236-236: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
273-273: Do not catch blind exception: Exception
(BLE001)
274-274: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
src/one_dragon/base/push/channel/push_plus.py
48-48: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
48-48: String contains ambiguous ; (FULLWIDTH SEMICOLON). Did you mean ; (SEMICOLON)?
(RUF001)
48-48: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
78-78: Unused method argument: image
(ARG002)
79-79: Unused method argument: proxy_url
(ARG002)
85-85: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
88-88: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
88-88: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
121-121: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
131-131: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
135-135: Do not catch blind exception: Exception
(BLE001)
136-136: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/push_config.py
98-98: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
src/one_dragon/base/push/channel/feishu.py
57-57: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
65-65: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
83-83: Unused method argument: proxy_url
(ARG002)
89-89: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
92-92: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
92-92: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
92-92: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
113-113: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
121-121: Comment contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF003)
121-121: Comment contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF003)
164-164: Do not catch blind exception: Exception
(BLE001)
165-165: Use explicit conversion flag
Replace with conversion flag
(RUF010)
178-178: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
234-234: Consider moving this statement to an else block
(TRY300)
236-236: Do not catch blind exception: Exception
(BLE001)
src/one_dragon/base/push/channel/ntfy.py
67-67: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
84-84: Unused method argument: proxy_url
(ARG002)
90-90: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
93-93: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
93-93: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
157-157: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
157-157: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
161-161: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
163-163: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
165-165: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
167-167: Do not catch blind exception: Exception
(BLE001)
168-168: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/curl_generator.py
22-22: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
25-25: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
51-51: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
63-63: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
131-131: Local variable e is assigned to but never used
Remove assignment to unused variable e
(F841)
132-132: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
155-155: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
155-155: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
187-187: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
189-189: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
193-193: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
198-198: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
210-210: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
230-230: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
246-246: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
251-251: Comment contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF003)
260-260: Unused method argument: style
(ARG002)
262-262: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
262-262: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
266-266: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
266-266: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
src/one_dragon/base/push/channel/push_deer.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
49-49: Unused method argument: image
(ARG002)
50-50: Unused method argument: proxy_url
(ARG002)
56-56: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
59-59: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
59-59: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
94-94: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
96-96: Do not catch blind exception: Exception
(BLE001)
98-98: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/channel/push_me.py
4-4: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
50-50: Unused method argument: image
(ARG002)
51-51: Unused method argument: proxy_url
(ARG002)
57-57: Docstring contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF002)
60-60: Docstring contains ambiguous ( (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?
(RUF002)
60-60: Docstring contains ambiguous ) (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?
(RUF002)
93-93: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
93-93: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
95-95: Do not catch blind exception: Exception
(BLE001)
97-97: String contains ambiguous : (FULLWIDTH COLON). Did you mean : (COLON)?
(RUF001)
97-97: Use explicit conversion flag
Replace with conversion flag
(RUF010)
src/one_dragon/base/push/push_channel.py
59-59: Undefined name MatLike
(F821)
85-85: Undefined name MatLike
(F821)
100-100: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
121-121: Undefined name MatLike
(F821)
🔇 Additional comments (20)
src/one_dragon/base/push/push_email_services.py (1)
426-433: 代码变更正确仅包含格式调整(添加空行和文件末尾换行符),无功能性变更。
src/script_chainer/win_exe/script_runner.py (1)
245-254: 推送调用正确添加了空值检查在调用
ctx.push_service.push_async前正确检查了ctx is not None,避免了潜在的空指针异常。src/one_dragon/base/push/channel/we_plus_bot.py (3)
17-51: 配置架构定义清晰TOKEN、RECEIVER、VERSION 字段的配置架构定义合理,必填项标记正确。
53-113: 推送逻辑实现正确实现要点:
- 推送前验证配置(第 79-81 行)
- 根据内容长度自动选择模板(800 字符阈值)
- 15 秒超时设置合理
- 响应状态检查正确
异常捕获用于提高推送操作的容错性,符合设计预期。
115-134: 配置验证逻辑完善正确验证了必填字段 TOKEN 和 RECEIVER 的非空性。
src/one_dragon/base/push/channel/synology_chat.py (2)
47-95: 推送逻辑基本结构正确配置验证、超时设置(15秒)、状态码检查和异常处理的整体结构合理。但请确保 API 调用格式正确(见前一条评论)。
97-116: 配置验证逻辑完善正确验证了 URL 和 TOKEN 的非空性。
src/one_dragon/base/push/channel/q_msg.py (2)
16-43: 配置架构定义合理KEY 和 TYPE 字段定义清晰,TYPE 使用 COMBO 类型并限定选项为 "send" 和 "group",设计合理。
98-120: 配置验证逻辑完善正确验证了 KEY 和 TYPE 的非空性,并强制要求 TYPE 必须是 "send" 或 "group",与配置架构中的 options 定义一致。
src/one_dragon/base/push/push_channel_config.py (2)
12-17: 字段类型枚举定义完整FieldTypeEnum 涵盖了常见的 UI 组件类型(TEXT、COMBO、KEY_VALUE、CODE_EDITOR),设计合理。
20-35: 数据类设计规范PushChannelConfigField 使用 dataclass 定义清晰,要点:
- 正确使用
field(default_factory=list)避免可变默认值问题- 字段类型和默认值设置合理
- Optional[str] 用于可选的 language 字段符合设计需求
src/one_dragon/base/push/channel/ai_botk.py (2)
12-47: 配置架构定义清晰初始化方法正确定义了所需的配置字段,字段类型和验证规则设置合理。
114-140: 配置验证逻辑完善验证方法正确检查了所有必需字段,并对目标类型进行了枚举值验证。
src/one_dragon/base/push/channel/server_chan.py (1)
27-78: 推送方法实现正确使用
requests.post的json参数发送数据是正确的做法,错误处理也较为完善。src/one_dragon/base/push/channel/dingding.py (2)
39-55: 签名生成方法实现正确HMAC-SHA256 签名逻辑符合钉钉机器人的签名要求。
90-97: 注意消息数据结构Line 93 将标题和内容都放入
title字段,Line 95 的text字段格式化为 Markdown。建议验证这是否符合实际需求,通常title应仅包含标题。请确认钉钉通知的实际显示效果是否符合预期。如果系统通知栏显示异常,可能需要调整数据结构。
src/one_dragon/base/push/channel/push_deer.py (1)
100-115: 配置验证实现正确验证逻辑正确检查必需的 KEY 字段。
src/one_dragon/base/push/channel/gotify.py (1)
82-95: 验证 Gotify API 数据格式Line 93 使用
data=data发送表单数据。请确认 Gotify API 是否接受application/x-www-form-urlencoded格式,还是需要 JSON 格式(json=data)。根据 Gotify 官方文档,消息推送端点通常接受 JSON 格式。如果 API 期望 JSON,请应用以下更改:
try: - response = requests.post(full_url, data=data, timeout=15) + response = requests.post(full_url, json=data, timeout=15)src/one_dragon/base/push/channel/wx_pusher.py (2)
77-96: ID 解析逻辑实现良好正确处理分号分隔的 ID 字符串,并验证至少配置一个推送目标。由于在 Line 69 已调用
validate_config,格式验证已完成,此处的类型转换是安全的。
98-107: HTML 内容格式化合理使用 HTML 格式化消息内容,
white-space: pre-wrap样式保留了换行符,提供良好的显示效果。
| data = { | ||
| "title": title, | ||
| "body": content, | ||
| } | ||
|
|
||
| # 添加可选参数 | ||
| archive = config.get('ARCHIVE') | ||
| if archive: | ||
| data["isArchive"] = archive | ||
|
|
||
| group = config.get('GROUP') | ||
| if group: | ||
| data["group"] = group | ||
|
|
||
| sound = config.get('SOUND') | ||
| if sound: | ||
| data["sound"] = sound | ||
|
|
||
| icon = config.get('ICON') | ||
| if icon: | ||
| data["icon"] = icon | ||
|
|
||
| level = config.get('LEVEL') | ||
| if level: | ||
| data["level"] = level | ||
|
|
||
| url_param = config.get('URL') | ||
| if url_param: | ||
| data["url"] = url_param | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
补充 DEVICE_KEY 的请求负载
Line 118 附近虽然给出了 DEVICE_KEY 配置项,但在构造 data 时没有带上它,导致填写的设备码被忽略,无法按设备定向推送。请补上该字段写入逻辑,恢复这一功能。
@@
data = {
"title": title,
"body": content,
}
+ device_key = config.get('DEVICE_KEY')
+ if device_key:
+ data["device_key"] = device_key📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| data = { | |
| "title": title, | |
| "body": content, | |
| } | |
| # 添加可选参数 | |
| archive = config.get('ARCHIVE') | |
| if archive: | |
| data["isArchive"] = archive | |
| group = config.get('GROUP') | |
| if group: | |
| data["group"] = group | |
| sound = config.get('SOUND') | |
| if sound: | |
| data["sound"] = sound | |
| icon = config.get('ICON') | |
| if icon: | |
| data["icon"] = icon | |
| level = config.get('LEVEL') | |
| if level: | |
| data["level"] = level | |
| url_param = config.get('URL') | |
| if url_param: | |
| data["url"] = url_param | |
| data = { | |
| "title": title, | |
| "body": content, | |
| } | |
| device_key = config.get('DEVICE_KEY') | |
| if device_key: | |
| data["device_key"] = device_key | |
| # 添加可选参数 | |
| archive = config.get('ARCHIVE') | |
| if archive: | |
| data["isArchive"] = archive | |
| group = config.get('GROUP') | |
| if group: | |
| data["group"] = group | |
| sound = config.get('SOUND') | |
| if sound: | |
| data["sound"] = sound | |
| icon = config.get('ICON') | |
| if icon: | |
| data["icon"] = icon | |
| level = config.get('LEVEL') | |
| if level: | |
| data["level"] = level | |
| url_param = config.get('URL') | |
| if url_param: | |
| data["url"] = url_param |
🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/bark.py around lines 113 to 142, the payload
construction omits the DEVICE_KEY config so device-specific pushes are ignored;
read config.get('DEVICE_KEY') (or 'DEVICE') as used elsewhere and, if present,
add it into the data dict (e.g., data["device_key"] or the expected Bark field
name) so the request includes the device target; keep the same conditional
pattern used for other optional fields and ensure the key name matches the API
contract.
| headers = {"Content-Type": "application/json;charset=utf-8"} | ||
| response = requests.post( | ||
| url=url, | ||
| data=json.dumps(data), | ||
| headers=headers, | ||
| timeout=15 | ||
| ).json() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
尊重 proxy_url 参数以支持受限网络
Line 146 发起请求时没有带上 proxy_url,导致在需要代理的环境中该通道完全不可用。请复用基类的 get_proxy 并把结果传给 requests.post。
@@
- response = requests.post(
- url=url,
- data=json.dumps(data),
- headers=headers,
- timeout=15
- ).json()
+ proxies = self.get_proxy(proxy_url)
+ response = requests.post(
+ url=url,
+ data=json.dumps(data),
+ headers=headers,
+ timeout=15,
+ proxies=proxies,
+ ).json()🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/bark.py around lines 144 to 150, the
requests.post call doesn't use the proxy_url, so the channel fails in restricted
networks; call the class/base get_proxy method to obtain the proxy dict (if any)
and pass it to requests.post via the proxies parameter (or omit if None),
ensuring the proxy dict is reused and the request includes timeout, headers and
data as before.
| api_url = f'{url}/api/message/send' | ||
| message_content = f"{title}\n{content}" | ||
| success_count = 0 | ||
| total_count = 0 | ||
|
|
||
| # 发送个人消息 (chatType=1) | ||
| for user_id in user_ids: | ||
| total_count += 1 | ||
| data = { | ||
| "peer": {"chatType": 1, "peerUin": user_id}, | ||
| "elements": [ | ||
| { | ||
| "elementType": 1, | ||
| "textElement": {"content": message_content}, | ||
| } | ||
| ], | ||
| } | ||
|
|
||
| if self._send_message(api_url, headers, data): | ||
| success_count += 1 | ||
| log.info(f"Chronocat QQ个人消息:{user_id} 推送成功") | ||
| else: | ||
| log.error(f"Chronocat QQ个人消息:{user_id} 推送失败") | ||
|
|
||
| # 发送群消息 (chatType=2) | ||
| for group_id in group_ids: | ||
| total_count += 1 | ||
| data = { | ||
| "peer": {"chatType": 2, "peerUin": group_id}, | ||
| "elements": [ | ||
| { | ||
| "elementType": 1, | ||
| "textElement": {"content": message_content}, | ||
| } | ||
| ], | ||
| } | ||
|
|
||
| if self._send_message(api_url, headers, data): | ||
| success_count += 1 | ||
| log.info(f"Chronocat QQ群消息:{group_id} 推送成功") | ||
| else: | ||
| log.error(f"Chronocat QQ群消息:{group_id} 推送失败") | ||
|
|
||
| if success_count == 0: | ||
| return False, "所有消息推送失败" | ||
| elif success_count < total_count: | ||
| return True, f"部分推送成功 ({success_count}/{total_count})" | ||
| else: | ||
| return True, "推送成功" | ||
|
|
||
| except Exception as e: | ||
| log.error("Chronocat 推送异常", exc_info=True) | ||
| return False, f"Chronocat 推送异常: {str(e)}" | ||
|
|
||
| def validate_config(self, config: dict[str, str]) -> tuple[bool, str]: | ||
| """ | ||
| 验证 Chronocat 配置 | ||
|
|
||
| Args: | ||
| config: 配置字典 | ||
|
|
||
| Returns: | ||
| tuple[bool, str]: 验证是否通过、错误信息 | ||
| """ | ||
| url = config.get('URL', '') | ||
| token = config.get('TOKEN', '') | ||
| qq_config = config.get('QQ', '') | ||
|
|
||
| if len(url) == 0: | ||
| return False, "服务地址不能为空" | ||
|
|
||
| if len(token) == 0: | ||
| return False, "访问令牌不能为空" | ||
|
|
||
| if len(qq_config) == 0: | ||
| return False, "QQ 配置不能为空" | ||
|
|
||
| # 验证 QQ 配置格式 | ||
| user_ids = self._extract_user_ids(qq_config) | ||
| group_ids = self._extract_group_ids(qq_config) | ||
|
|
||
| if not user_ids and not group_ids: | ||
| return False, "QQ 配置格式错误,需要包含 user_id 或 group_id" | ||
|
|
||
| return True, "配置验证通过" | ||
|
|
||
| def _extract_user_ids(self, qq_config: str) -> List[str]: | ||
| """ | ||
| 从 QQ 配置中提取用户 ID | ||
|
|
||
| Args: | ||
| qq_config: QQ 配置字符串 | ||
|
|
||
| Returns: | ||
| List[str]: 用户 ID 列表 | ||
| """ | ||
| return re.findall(r"user_id=(\d+)", qq_config) | ||
|
|
||
| def _extract_group_ids(self, qq_config: str) -> List[str]: | ||
| """ | ||
| 从 QQ 配置中提取群 ID | ||
|
|
||
| Args: | ||
| qq_config: QQ 配置字符串 | ||
|
|
||
| Returns: | ||
| List[str]: 群 ID 列表 | ||
| """ | ||
| return re.findall(r"group_id=(\d+)", qq_config) | ||
|
|
||
| def _send_message(self, url: str, headers: dict[str, str], data: dict) -> bool: | ||
| """ | ||
| 发送单条消息 | ||
|
|
||
| Args: | ||
| url: API 地址 | ||
| headers: 请求头 | ||
| data: 请求数据 | ||
|
|
||
| Returns: | ||
| bool: 是否发送成功 | ||
| """ | ||
| try: | ||
| response = requests.post(url, headers=headers, data=json.dumps(data), timeout=15) | ||
| return response.status_code == 200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为 Chronocat 渠道补充代理能力
push 接收了 proxy_url,但 _send_message 内的请求没有使用代理,部署在需要代理才能访问 Chronocat 服务的网络时会完全失败。
headers = {
"Content-Type": "application/json",
"Authorization": f'Bearer {token}',
}
api_url = f'{url}/api/message/send'
message_content = f"{title}\n{content}"
+ proxies = self.get_proxy(proxy_url)
@@
- if self._send_message(api_url, headers, data):
+ if self._send_message(api_url, headers, data, proxies):
@@
- if self._send_message(api_url, headers, data):
+ if self._send_message(api_url, headers, data, proxies):
@@
- def _send_message(self, url: str, headers: dict[str, str], data: dict) -> bool:
+ def _send_message(
+ self,
+ url: str,
+ headers: dict[str, str],
+ data: dict,
+ proxies: dict | None,
+ ) -> bool:
"""
发送单条消息
"""
try:
- response = requests.post(url, headers=headers, data=json.dumps(data), timeout=15)
+ response = requests.post(
+ url,
+ headers=headers,
+ data=json.dumps(data),
+ timeout=15,
+ proxies=proxies,
+ )
return response.status_code == 200Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.14.2)
150-150: Do not catch blind exception: Exception
(BLE001)
152-152: Use explicit conversion flag
Replace with conversion flag
(RUF010)
182-182: String contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF001)
224-224: Consider moving this statement to an else block
(TRY300)
| if image is not None: | ||
| image_data = self.image_to_bytes(image) | ||
| if image_data is not None: | ||
| image_data.seek(0) | ||
| files = {'file': ('image.png', image_data, 'image/png')} | ||
| data = {'payload_json': json.dumps(message_payload)} | ||
| if "Content-Type" in headers: | ||
| del headers["Content-Type"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
修正上传图片的文件类型声明
image_to_bytes 最终产出的是 JPEG 数据,但这里把文件名/类型写成了 PNG,Discord 会根据头信息解析,可能导致上传失败或显示异常。请调整为正确的 JPEG 标识。
- files = {'file': ('image.png', image_data, 'image/png')}
+ files = {'file': ('image.jpg', image_data, 'image/jpeg')}🧰 Tools
🪛 Ruff (0.14.2)
125-125: Use pop instead of key in dict followed by del dict[key]
Replace if statement with .pop(..., None)
(RUF051)
🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/discord.py around lines 118 to 125, the
uploaded image is actually JPEG data but the code labels it as PNG; change the
files entry to use a .jpg/.jpeg filename and the MIME type image/jpeg (e.g.
('image.jpg', image_data, 'image/jpeg')) so Discord parses the upload correctly,
keeping the existing payload_json handling and removal of the Content-Type
header.
| response = requests.post(message_url, headers=headers, data=data, files=files, timeout=30) | ||
| response.raise_for_status() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
请确保消息发送同样走代理链路
创建私聊频道时已走 proxies,但真正发送消息的请求忽略了代理配置,在需要代理才能访问 Discord 的环境中会直接失败。请把相同的 proxies 参数传入消息发送调用。
- response = requests.post(message_url, headers=headers, data=data, files=files, timeout=30)
+ response = requests.post(
+ message_url,
+ headers=headers,
+ data=data,
+ files=files,
+ timeout=30,
+ proxies=proxies,
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| response = requests.post(message_url, headers=headers, data=data, files=files, timeout=30) | |
| response.raise_for_status() | |
| response = requests.post( | |
| message_url, | |
| headers=headers, | |
| data=data, | |
| files=files, | |
| timeout=30, | |
| proxies=proxies, | |
| ) | |
| response.raise_for_status() |
🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/discord.py around lines 133 to 134, the POST
that sends the actual Discord message omits the proxies argument so it bypasses
the proxy chain used when creating the DM channel; update the requests.post call
to pass the same proxies variable (proxies=proxies) used earlier so the message
send uses the configured proxy and include it in the call signature along with
the existing headers, data, files and timeout.
| # 解析服务器地址和端口 | ||
| host, port = server.split(":") | ||
| port_int = int(port) if port else None | ||
| # 连接SMTP服务器并发送邮件 | ||
| smtp_server = ( | ||
| smtplib.SMTP_SSL(host, port_int) if use_ssl | ||
| else smtplib.SMTP(host, port_int) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
端口解析可能失败
如果服务器配置为 host: 格式(有冒号但端口为空),Line 124 的 int(port) 将抛出 ValueError。虽然 validate_config 检查了冒号的存在,但没有验证端口部分非空。
建议在 push 方法中添加错误处理,或在验证方法中增强检查:
# 检查服务器格式
if ":" not in server:
return False, "邮件服务器格式不正确,应包含端口号(如:smtp.exmail.qq.com:465)"
try:
host, port = server.split(":")
+ if not port.strip():
+ return False, "端口号不能为空"
port_int = int(port)
if port_int < 1 or port_int > 65535:
return False, "端口号必须在1-65535之间"Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/smtp.py around lines 122-129, the code calls
int(port) directly which will raise ValueError for inputs like "host:" where the
port substring is empty; modify the parsing to check that the port part is
non-empty (e.g., if port and port.strip(): port_int = int(port) else port_int =
None) and either fallback to a sensible default or raise a clear configuration
error; alternatively strengthen validate_config to reject empty port segments so
this code can assume a valid numeric port — ensure any ValueError is caught and
reraised or logged with a descriptive message referencing the invalid server
string.
| # 构建完整的请求URL | ||
| full_url = url + token | ||
|
|
||
| # 构建请求数据 | ||
| message_text = f"{title}\n{content}" | ||
| payload_data = {"text": message_text} | ||
| data = "payload=" + json.dumps(payload_data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
请验证 URL 拼接格式
第 77 行直接拼接 URL 和 Token(full_url = url + token)可能存在问题。通常 API 需要路径分隔符或查询参数格式。请确认 Synology Chat API 的正确 URL 格式。
第 82 行的 payload 格式("payload=" + json.dumps(...))也较为特殊,请确保这符合 Synology Chat 的 API 规范。
建议验证 Synology Chat 官方文档中的 API 调用格式:
🌐 Web query:
Synology Chat webhook API URL format and payload structure
💡 Result:
Summary — URL format
- Incoming webhook endpoint:
https://:/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=&token=<YOUR_TOKEN>. [1][2]
HTTP method and payload
- Use POST. Synology accepts the message as either:
- form-encoded with a single field named payload whose value is a JSON string (payload={"text":"..."}), or
- a raw JSON body containing the payload (same JSON shape). Many examples use application/x-www-form-urlencoded with --data-urlencode 'payload=...'. You may also include the token in the POST body instead of the URL. [1][2]
Common payload fields (JSON)
- text: string (message text).
- file_url: URL to an image/file to attach (must be a reachable HTTP/HTTPS URL).
- user_ids: array of user IDs to target (depends on API version/usage).
- other fields (source, etc.) are used by some clients/libraries — follow what your integration UI shows. Example JSON: {"text":"This is a test","file_url":"https://example.com/img.jpg"}. [1][2][3]
Example curl (working)
- curl -X POST --data-urlencode 'payload={"text":"This is a test"}' 'https://HOST:PORT/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=2&token=YOUR_TOKEN' . [1][2]
Notes / tips
- Don't wrap the token in URL-encoded quotes — token is the raw token string. Some DSM versions accept version=1 or version=2; version=2 is commonly used. If you get parameter errors, ensure api/method/version/token are present and payload is properly encoded. [1][2][3]
Sources
[1] Stack Overflow — “bash - Synology chat incoming webhooks” (includes Synology KB excerpt and curl examples).
[2] GitHub synochat (incoming webhook examples).
[3] Synology Chat integration / technical docs (overview of incoming/outgoing webhooks).
修复第 77 行 URL 拼接格式
Synology Chat API 的 Token 应作为 URL 查询参数(token=<YOUR_TOKEN>),而非直接字符串拼接。
第 77 行当前代码 full_url = url + token 会生成格式错误的 URL。根据 Synology 官方文档,正确的 URL 格式应为:
https://<HOST>:<PORT>/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=<N>&token=<YOUR_TOKEN>
需要验证 url 变量是否已包含完整的 API 端点和 token= 前缀。若 url 为基础端点,应改为 full_url = url + "&token=" + token 或类似格式。
第 82 行的 payload 格式 "payload=" + json.dumps(...) 是正确的,符合 Synology Chat 的 application/x-www-form-urlencoded 规范。
🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/synology_chat.py around lines 76 to 82, the
code concatenates the token directly (full_url = url + token) producing an
invalid URL; change the construction to ensure the token is appended as a query
parameter (token=<YOUR_TOKEN>) by checking whether url already contains a '?'
and then appending either '?token=' or '&token=' followed by the token (or
explicitly append '&token=' if url already includes other query params); keep
the payload construction as-is ("payload=" + json.dumps(...)) for
application/x-www-form-urlencoded.
| # 1. 先发文字 | ||
| text_data = {"msgtype": "text", "text": {"content": f"{title}\n{content}"}} | ||
| try: | ||
| resp_obj = requests.post(url, data=json.dumps(text_data), headers=headers, timeout=15) | ||
| resp_obj.raise_for_status() | ||
|
|
||
| status = resp_obj.status_code | ||
| body_snip = (resp_obj.text or "")[:300] if hasattr(resp_obj, "text") else "" | ||
| resp = None | ||
|
|
||
| try: | ||
| resp = resp_obj.json() | ||
| except Exception as je: | ||
| error_msg = f"企业微信机器人文字响应解析失败: {type(je).__name__}: {je}; status={status}; body_snip={body_snip}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
|
|
||
| if resp and resp.get("errcode") == 0: | ||
| text_success = True | ||
| log.info("企业微信机器人文字推送成功!") | ||
| else: | ||
| errcode = resp.get("errcode") if resp else None | ||
| errmsg = resp.get("errmsg") if resp else None | ||
| error_msg = f"企业微信机器人文字推送失败! status={status}; errcode={errcode}; errmsg={errmsg}; body_snip={body_snip}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
| except Exception as e: | ||
| error_msg = f"企业微信机器人文字推送请求异常: {type(e).__name__}: {e}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
|
|
||
| # 2. 再发图片 | ||
| if image is not None: | ||
| image_success = self._send_image(url, headers, image, error_messages) | ||
| else: | ||
| image_success = True | ||
|
|
||
| # 判断整体结果 | ||
| if text_success and image_success: | ||
| return True, "推送成功" | ||
| elif text_success or image_success: | ||
| return True, f"部分推送成功: {'; '.join(error_messages)}" if error_messages else "部分推送成功" | ||
| else: | ||
| return False, f"推送失败: {'; '.join(error_messages)}" if error_messages else "推送失败" | ||
|
|
||
| except Exception as e: | ||
| return False, f"企业微信推送异常: {str(e)}" | ||
|
|
||
| def _send_image(self, url: str, headers: dict[str, str], image: MatLike, error_messages: list[str]) -> bool: | ||
| """ | ||
| 发送图片到企业微信机器人 | ||
|
|
||
| Args: | ||
| url: 机器人URL | ||
| headers: 请求头 | ||
| image: 图片数据 | ||
| error_messages: 错误消息列表 | ||
|
|
||
| Returns: | ||
| bool: 是否发送成功 | ||
| """ | ||
| # 企业微信机器人图片最大支持2MB | ||
| TARGET_SIZE = 2 * 1024 * 1024 | ||
| img_bytes = self.image_to_bytes(image, max_bytes=TARGET_SIZE) | ||
| if img_bytes is None: | ||
| error_msg = "图片转换失败" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
| return False | ||
| img_bytes = img_bytes.getvalue() | ||
| img_size = len(img_bytes) | ||
| img_base64 = base64.b64encode(img_bytes).decode('utf-8') | ||
| img_md5 = hashlib.md5(img_bytes).hexdigest() | ||
| img_data = { | ||
| "msgtype": "image", | ||
| "image": {"base64": img_base64, "md5": img_md5} | ||
| } | ||
|
|
||
| resp_obj = requests.post(url, data=json.dumps(img_data), headers=headers, timeout=15) | ||
| status = resp_obj.status_code | ||
| body_snip = (resp_obj.text or "")[:300] if hasattr(resp_obj, "text") else "" | ||
|
|
||
| try: | ||
| resp = resp_obj.json() | ||
| except Exception as je: | ||
| error_msg = f"企业微信机器人图片响应解析失败: {type(je).__name__}: {je}; status={status}; body_snip={body_snip}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
| return False | ||
|
|
||
| if resp and resp.get("errcode") == 0: | ||
| log.info("企业微信机器人图片推送成功!") | ||
| return True | ||
| else: | ||
| errcode = resp.get("errcode") if resp else None | ||
| errmsg = resp.get("errmsg") if resp else None | ||
| error_msg = f"企业微信机器人图片推送失败! status={status}; errcode={errcode}; errmsg={errmsg}; size={img_size}B; body_snip={body_snip}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
补齐企业微信渠道的代理支持
文本和图片请求都没有把 proxy_url 传入,代理网络下两种消息都会失败。同时 _send_image 需要能接收同一个代理配置。
headers = {"Content-Type": "application/json;charset=utf-8"}
+ proxies = self.get_proxy(proxy_url)
text_success = False
image_success = False
error_messages = []
# 1. 先发文字
@@
- resp_obj = requests.post(url, data=json.dumps(text_data), headers=headers, timeout=15)
+ resp_obj = requests.post(
+ url,
+ data=json.dumps(text_data),
+ headers=headers,
+ timeout=15,
+ proxies=proxies,
+ )
@@
- image_success = self._send_image(url, headers, image, error_messages)
+ image_success = self._send_image(url, headers, image, error_messages, proxies)
@@
- def _send_image(self, url: str, headers: dict[str, str], image: MatLike, error_messages: list[str]) -> bool:
+ def _send_image(
+ self,
+ url: str,
+ headers: dict[str, str],
+ image: MatLike,
+ error_messages: list[str],
+ proxies: dict | None,
+ ) -> bool:
@@
- resp_obj = requests.post(url, data=json.dumps(img_data), headers=headers, timeout=15)
+ resp_obj = requests.post(
+ url,
+ data=json.dumps(img_data),
+ headers=headers,
+ timeout=15,
+ proxies=proxies,
+ )Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.14.2)
97-97: Do not catch blind exception: Exception
(BLE001)
104-104: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
111-111: Do not catch blind exception: Exception
(BLE001)
130-130: Do not catch blind exception: Exception
(BLE001)
131-131: Use explicit conversion flag
Replace with conversion flag
(RUF010)
133-133: Undefined name MatLike
(F821)
157-157: Probable use of insecure hash functions in hashlib: md5
(S324)
169-169: Do not catch blind exception: Exception
(BLE001)
176-176: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
| def _send_image(self, url: str, headers: dict[str, str], image: MatLike, error_messages: list[str]) -> bool: | ||
| """ | ||
| 发送图片到企业微信机器人 | ||
|
|
||
| Args: | ||
| url: 机器人URL | ||
| headers: 请求头 | ||
| image: 图片数据 | ||
| error_messages: 错误消息列表 | ||
|
|
||
| Returns: | ||
| bool: 是否发送成功 | ||
| """ | ||
| # 企业微信机器人图片最大支持2MB | ||
| TARGET_SIZE = 2 * 1024 * 1024 | ||
| img_bytes = self.image_to_bytes(image, max_bytes=TARGET_SIZE) | ||
| if img_bytes is None: | ||
| error_msg = "图片转换失败" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
| return False | ||
| img_bytes = img_bytes.getvalue() | ||
| img_size = len(img_bytes) | ||
| img_base64 = base64.b64encode(img_bytes).decode('utf-8') | ||
| img_md5 = hashlib.md5(img_bytes).hexdigest() | ||
| img_data = { | ||
| "msgtype": "image", | ||
| "image": {"base64": img_base64, "md5": img_md5} | ||
| } | ||
|
|
||
| resp_obj = requests.post(url, data=json.dumps(img_data), headers=headers, timeout=15) | ||
| status = resp_obj.status_code | ||
| body_snip = (resp_obj.text or "")[:300] if hasattr(resp_obj, "text") else "" | ||
|
|
||
| try: | ||
| resp = resp_obj.json() | ||
| except Exception as je: | ||
| error_msg = f"企业微信机器人图片响应解析失败: {type(je).__name__}: {je}; status={status}; body_snip={body_snip}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
| return False | ||
|
|
||
| if resp and resp.get("errcode") == 0: | ||
| log.info("企业微信机器人图片推送成功!") | ||
| return True | ||
| else: | ||
| errcode = resp.get("errcode") if resp else None | ||
| errmsg = resp.get("errmsg") if resp else None | ||
| error_msg = f"企业微信机器人图片推送失败! status={status}; errcode={errcode}; errmsg={errmsg}; size={img_size}B; body_snip={body_snip}" | ||
| error_messages.append(error_msg) | ||
| log.error(error_msg) | ||
| return False | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MatLike 未定义会导致模块导入失败
_send_image 的类型注解直接使用了 MatLike,但本文件并未导入该名称,在没有 from __future__ import annotations 的情况下会在模块加载阶段抛出 NameError。请补充正确的引用。
import hashlib
import json
import requests
+from cv2.typing import MatLike
from one_dragon.base.push.push_channel import PushChannelCommittable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.14.2)
133-133: Undefined name MatLike
(F821)
157-157: Probable use of insecure hash functions in hashlib: md5
(S324)
169-169: Do not catch blind exception: Exception
(BLE001)
176-176: String contains ambiguous ! (FULLWIDTH EXCLAMATION MARK). Did you mean ! (EXCLAMATION MARK)?
(RUF001)
🤖 Prompt for AI Agents
In src/one_dragon/base/push/channel/work_weixin.py around lines 133 to 185, the
type annotation MatLike is used but not defined or imported which will raise
NameError at module import time; fix by adding "from __future__ import
annotations" at the top of the file so forward references are allowed (or
alternatively import MatLike from its defining module if you know its source)
and run linters/tests to confirm the module imports correctly.
| def image_to_bytes(self, image: MatLike, max_bytes: int | None = None) -> BytesIO | None: | ||
| """ | ||
| 将图片转换为字节数组 | ||
|
|
||
| Args: | ||
| image: 图片 RGB格式 | ||
| max_bytes: 图片最大字节数 超过时压缩 | ||
|
|
||
| Returns: | ||
| BytesIO: 图片数据 统一jpeg格式 | ||
| """ | ||
| bgr_image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | ||
| retval, buffer = cv2.imencode('.jpg', bgr_image) | ||
|
|
||
| if retval: | ||
| image_bytes = BytesIO(buffer.tobytes()) | ||
| if max_bytes is not None: | ||
| img_bytes = image_bytes.getvalue() | ||
| orig_size = len(img_bytes) | ||
| if orig_size > max_bytes: | ||
| return self._compress_image_bytes(bgr_image, max_bytes) | ||
|
|
||
| return image_bytes | ||
| else: | ||
| return None | ||
|
|
||
| def _compress_image_bytes(self, bgr_image: MatLike, max_bytes: int) -> BytesIO | None: | ||
| """ | ||
| 自动将图片压缩为渐进式 JPG,使用二分搜索质量,尽量贴近 2MB 上限 | ||
|
|
||
| Args: | ||
| bgr_image: 图片数据 | ||
| max_bytes: 最大字节数 | ||
|
|
||
| Returns: | ||
| BytesIO: 压缩后的图片数据 统一jpeg格式 | ||
| """ | ||
|
|
||
| import cv2 | ||
| best: BytesIO | None = None | ||
|
|
||
| # 二分搜索质量,尽量贴近 2MB | ||
| lo, hi = 30, 90 | ||
| while lo <= hi: | ||
| q = (lo + hi) // 2 | ||
| params = [ | ||
| int(cv2.IMWRITE_JPEG_QUALITY), int(q), | ||
| int(cv2.IMWRITE_JPEG_OPTIMIZE), 1, | ||
| int(cv2.IMWRITE_JPEG_PROGRESSIVE), 1, | ||
| ] | ||
| ok, enc = cv2.imencode('.jpg', bgr_image, params) | ||
| if not ok: | ||
| break | ||
| size = enc.nbytes | ||
| if size <= max_bytes: | ||
| best = BytesIO(enc.tobytes()) | ||
| lo = q + 1 # 尝试更高质量 | ||
| else: | ||
| hi = q - 1 # 降低质量 | ||
|
|
||
| return best | ||
|
|
||
| def image_to_base64(self, image: MatLike, max_bytes: int | None = None) -> str | None: | ||
| """ | ||
| 将图片转换为 base64 字符串 | ||
|
|
||
| Args: | ||
| image: 图片 | ||
| max_bytes: 图片最大字节数 超过时压缩 | ||
|
|
||
| Returns: | ||
| str: 图片 base64 字符串 | ||
| """ | ||
| image_bytes = self.image_to_bytes(image, max_bytes=max_bytes) | ||
| if image_bytes is None: | ||
| return None | ||
| image_bytes.seek(0) | ||
| return base64.b64encode(image_bytes.getvalue()).decode('utf-8') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
阻止模块导入失败:补齐 MatLike 依赖
MatLike 在多个函数注解中使用,但当前文件既没有导入也没有定义。Python 会在函数定义阶段解析这些注解,实际加载模块时会直接抛出 NameError: name 'MatLike' is not defined,导致整个推送模块无法初始化。请显式引入 MatLike,并为不支持该类型提示的旧版 OpenCV 提供回退,确保模块始终能被导入。
@@
-import base64
-from abc import abstractmethod, ABC
-from io import BytesIO
-
-import cv2
-
-from one_dragon.base.push.push_channel_config import PushChannelConfigField
+import base64
+from abc import abstractmethod, ABC
+from io import BytesIO
+from typing import Any
+
+import cv2
+
+try:
+ from cv2.typing import MatLike
+except (ImportError, AttributeError):
+ MatLike = Any
+
+from one_dragon.base.push.push_channel_config import PushChannelConfigFieldCommittable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.14.2)
59-59: Undefined name MatLike
(F821)
85-85: Undefined name MatLike
(F821)
100-100: Comment contains ambiguous , (FULLWIDTH COMMA). Did you mean , (COMMA)?
(RUF003)
121-121: Undefined name MatLike
(F821)
🤖 Prompt for AI Agents
In src/one_dragon/base/push/push_channel.py around lines 59 to 136, the type
name MatLike used in function annotations is not imported or defined which
raises NameError at import time; fix by adding a robust import/fallback at the
top of the module: try to import MatLike (preferably from numpy.typing or OpenCV
typing if available), and if that fails define MatLike = typing.Any (or use
typing.TYPE_CHECKING guard and conditional imports) so annotations remain valid
and the module can always be imported; update imports to include
typing.Any/TYPE_CHECKING and ensure no runtime dependency on MatLike resolution.
Summary by CodeRabbit
发版说明
New Features
Refactor