vibego 0.2.35__py3-none-any.whl → 0.2.37__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.
Potentially problematic release.
This version of vibego might be problematic. Click here for more details.
- bot.py +154 -57
- {vibego-0.2.35.dist-info → vibego-0.2.37.dist-info}/METADATA +1 -1
- {vibego-0.2.35.dist-info → vibego-0.2.37.dist-info}/RECORD +7 -7
- vibego_cli/__init__.py +1 -1
- {vibego-0.2.35.dist-info → vibego-0.2.37.dist-info}/WHEEL +0 -0
- {vibego-0.2.35.dist-info → vibego-0.2.37.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.35.dist-info → vibego-0.2.37.dist-info}/top_level.txt +0 -0
bot.py
CHANGED
|
@@ -482,6 +482,11 @@ _ESCAPED_CODE_BLOCK_PATTERN = re.compile(
|
|
|
482
482
|
re.DOTALL
|
|
483
483
|
)
|
|
484
484
|
|
|
485
|
+
_OPTIMIZE_CHECKBOX_PATTERN = re.compile(r"\\\[\s+\\\]")
|
|
486
|
+
_OPTIMIZE_DOT_PATTERN = re.compile(
|
|
487
|
+
r"(?P<prefix>[0-9A-Za-z\u4e00-\u9fff])\\\.(?=(?:\s|$|[)\]\u3002\uff0c\uff1b\uff1a\uff01\uff1f]))"
|
|
488
|
+
)
|
|
489
|
+
|
|
485
490
|
|
|
486
491
|
def _is_already_escaped(text: str) -> bool:
|
|
487
492
|
"""检测文本是否已经包含 MarkdownV2 转义字符。
|
|
@@ -534,68 +539,46 @@ def _unescape_markdown_v2(text: str) -> str:
|
|
|
534
539
|
return re.sub(r"\\([_*\[\]()~`>#+=|{}.!:-])", r"\1", text)
|
|
535
540
|
|
|
536
541
|
|
|
537
|
-
def
|
|
538
|
-
"""
|
|
539
|
-
|
|
540
|
-
改进的分段处理策略:
|
|
541
|
-
1. 先识别已转义的代码块(\`\`\`...\`\`\` 和 \`...\`)
|
|
542
|
-
2. 对这些代码块先反转义边界反引号,变成正常代码块
|
|
543
|
-
3. 然后用正常的 CODE_SEGMENT_RE 识别代码块
|
|
544
|
-
4. 只对非代码块的普通文本进行反转义
|
|
545
|
-
5. 代码块内容保持转义状态(因为是代码本身)
|
|
546
|
-
6. 重新组合所有段落
|
|
547
|
-
|
|
548
|
-
Args:
|
|
549
|
-
text: 待处理的文本
|
|
550
|
-
|
|
551
|
-
Returns:
|
|
552
|
-
处理后的文本(如未检测到预转义,返回原文本)
|
|
553
|
-
"""
|
|
542
|
+
def _force_unescape_markdown(text: str) -> str:
|
|
543
|
+
"""强制移除 MarkdownV2 转义,同时保护代码块语法不被破坏。"""
|
|
554
544
|
if not text:
|
|
555
545
|
return text
|
|
556
546
|
|
|
557
|
-
# 快速检测:如果没有任何转义字符,直接返回
|
|
558
|
-
if not _is_already_escaped(text):
|
|
559
|
-
return text
|
|
560
|
-
|
|
561
|
-
# 第一步:处理已转义的代码块,将边界反引号反转义
|
|
562
|
-
# 这样后续可以用正常的 CODE_SEGMENT_RE 识别它们
|
|
563
547
|
processed = text
|
|
564
|
-
|
|
565
|
-
# 先标记所有已转义的代码块,用占位符替换
|
|
566
548
|
code_blocks: list[str] = []
|
|
567
549
|
|
|
568
550
|
def _preserve_code_block(match: re.Match[str]) -> str:
|
|
569
|
-
"""
|
|
551
|
+
"""临时替换代码块,防止内部字符被错误反转义。"""
|
|
570
552
|
block = match.group(0)
|
|
571
|
-
# 代码块边界的反引号需要反转义,但内容保持不变
|
|
572
|
-
# 例如:\`\`\`bash\npython -m vibego\_cli\`\`\`
|
|
573
|
-
# -> ```bash\npython -m vibego\_cli```
|
|
574
553
|
if block.startswith(r"\`\`\`"):
|
|
575
|
-
#
|
|
576
|
-
unescaped_block = block.replace(r"\`", "`", 6)
|
|
554
|
+
# 多行代码块保留内容,只修复边界反引号
|
|
555
|
+
unescaped_block = block.replace(r"\`", "`", 6)
|
|
577
556
|
else:
|
|
578
|
-
#
|
|
579
|
-
unescaped_block = block.replace(r"\`", "`", 2)
|
|
557
|
+
# 单行代码块同理处理首尾反引号
|
|
558
|
+
unescaped_block = block.replace(r"\`", "`", 2)
|
|
580
559
|
|
|
581
560
|
placeholder = f"__CODE_BLOCK_{len(code_blocks)}__"
|
|
582
561
|
code_blocks.append(unescaped_block)
|
|
583
562
|
return placeholder
|
|
584
563
|
|
|
585
564
|
processed = _ESCAPED_CODE_BLOCK_PATTERN.sub(_preserve_code_block, processed)
|
|
565
|
+
processed = _unescape_markdown_v2(processed)
|
|
586
566
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
processed = _unescape_markdown_v2(processed)
|
|
590
|
-
|
|
591
|
-
# 第三步:恢复代码块
|
|
592
|
-
for i, block in enumerate(code_blocks):
|
|
593
|
-
placeholder = f"__CODE_BLOCK_{i}__"
|
|
594
|
-
processed = processed.replace(placeholder, block)
|
|
567
|
+
for index, block in enumerate(code_blocks):
|
|
568
|
+
processed = processed.replace(f"__CODE_BLOCK_{index}__", block)
|
|
595
569
|
|
|
596
570
|
return processed
|
|
597
571
|
|
|
598
572
|
|
|
573
|
+
def _unescape_if_already_escaped(text: str) -> str:
|
|
574
|
+
"""智能检测并清理预转义文本,必要时触发强制反转义。"""
|
|
575
|
+
if not text:
|
|
576
|
+
return text
|
|
577
|
+
if not _is_already_escaped(text):
|
|
578
|
+
return text
|
|
579
|
+
return _force_unescape_markdown(text)
|
|
580
|
+
|
|
581
|
+
|
|
599
582
|
def _prepare_model_payload(text: str) -> str:
|
|
600
583
|
if _IS_MARKDOWN_V2:
|
|
601
584
|
cleaned = _unescape_if_already_escaped(text)
|
|
@@ -605,6 +588,47 @@ def _prepare_model_payload(text: str) -> str:
|
|
|
605
588
|
return text
|
|
606
589
|
|
|
607
590
|
|
|
591
|
+
def _optimize_markdown_v2_payload(payload: str) -> str:
|
|
592
|
+
"""在保留 MarkdownV2 合法性的前提下,移除常见的冗余反斜杠。"""
|
|
593
|
+
|
|
594
|
+
if not payload or "\\" not in payload:
|
|
595
|
+
return payload
|
|
596
|
+
|
|
597
|
+
preserved_segments: list[str] = []
|
|
598
|
+
|
|
599
|
+
def _preserve(segment: re.Match[str]) -> str:
|
|
600
|
+
placeholder = f"__OPT_CODE_BLOCK_{len(preserved_segments)}__"
|
|
601
|
+
preserved_segments.append(segment.group(0))
|
|
602
|
+
return placeholder
|
|
603
|
+
|
|
604
|
+
stripped = CODE_SEGMENT_RE.sub(_preserve, payload)
|
|
605
|
+
stripped = _OPTIMIZE_CHECKBOX_PATTERN.sub("[ ]", stripped)
|
|
606
|
+
|
|
607
|
+
def _dot_replacer(match: re.Match[str]) -> str:
|
|
608
|
+
prefix = match.group("prefix")
|
|
609
|
+
return f"{prefix}."
|
|
610
|
+
|
|
611
|
+
stripped = _OPTIMIZE_DOT_PATTERN.sub(_dot_replacer, stripped)
|
|
612
|
+
|
|
613
|
+
for index, segment in enumerate(preserved_segments):
|
|
614
|
+
stripped = stripped.replace(f"__OPT_CODE_BLOCK_{index}__", segment)
|
|
615
|
+
|
|
616
|
+
return stripped
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def _prepare_model_payload_variants(text: str) -> tuple[str, Optional[str]]:
|
|
620
|
+
"""返回首选与备用的 MarkdownV2 内容,首选尽量减少转义。"""
|
|
621
|
+
|
|
622
|
+
strict_payload = _prepare_model_payload(text)
|
|
623
|
+
if not _IS_MARKDOWN_V2:
|
|
624
|
+
return strict_payload, None
|
|
625
|
+
|
|
626
|
+
optimized_payload = _optimize_markdown_v2_payload(strict_payload)
|
|
627
|
+
if optimized_payload != strict_payload:
|
|
628
|
+
return optimized_payload, strict_payload
|
|
629
|
+
return strict_payload, None
|
|
630
|
+
|
|
631
|
+
|
|
608
632
|
def _extract_bad_request_message(exc: TelegramBadRequest) -> str:
|
|
609
633
|
message = getattr(exc, "message", None)
|
|
610
634
|
if not message:
|
|
@@ -658,6 +682,7 @@ async def _send_with_markdown_guard(
|
|
|
658
682
|
sender: Callable[[str], Awaitable[None]],
|
|
659
683
|
*,
|
|
660
684
|
raw_sender: Optional[Callable[[str], Awaitable[None]]] = None,
|
|
685
|
+
fallback_payload: Optional[str] = None,
|
|
661
686
|
) -> str:
|
|
662
687
|
try:
|
|
663
688
|
await sender(text)
|
|
@@ -666,6 +691,19 @@ async def _send_with_markdown_guard(
|
|
|
666
691
|
if not _is_markdown_parse_error(exc):
|
|
667
692
|
raise
|
|
668
693
|
|
|
694
|
+
if fallback_payload and fallback_payload != text:
|
|
695
|
+
try:
|
|
696
|
+
await sender(fallback_payload)
|
|
697
|
+
worker_log.debug(
|
|
698
|
+
"Markdown 优化回退为严格转义版本",
|
|
699
|
+
extra={"length": len(fallback_payload)},
|
|
700
|
+
)
|
|
701
|
+
return fallback_payload
|
|
702
|
+
except TelegramBadRequest as fallback_exc:
|
|
703
|
+
if not _is_markdown_parse_error(fallback_exc):
|
|
704
|
+
raise
|
|
705
|
+
exc = fallback_exc
|
|
706
|
+
|
|
669
707
|
sanitized: Optional[str]
|
|
670
708
|
if _IS_MARKDOWN_V2:
|
|
671
709
|
sanitized = _escape_markdown_v2(text)
|
|
@@ -695,7 +733,7 @@ async def _send_with_markdown_guard(
|
|
|
695
733
|
raise
|
|
696
734
|
|
|
697
735
|
# Markdown 彻底失败时退回纯文本发送,需要先清理 MarkdownV2 转义符号
|
|
698
|
-
fallback_payload =
|
|
736
|
+
fallback_payload = _force_unescape_markdown(text)
|
|
699
737
|
if fallback_payload != text:
|
|
700
738
|
worker_log.debug(
|
|
701
739
|
"Markdown 降级为纯文本发送,已移除转义符号",
|
|
@@ -762,7 +800,11 @@ async def reply_large_text(
|
|
|
762
800
|
"""
|
|
763
801
|
bot = current_bot()
|
|
764
802
|
parse_mode_value = parse_mode if parse_mode is not None else _parse_mode_value()
|
|
765
|
-
|
|
803
|
+
if preformatted:
|
|
804
|
+
prepared = text
|
|
805
|
+
fallback_payload = None
|
|
806
|
+
else:
|
|
807
|
+
prepared, fallback_payload = _prepare_model_payload_variants(text)
|
|
766
808
|
|
|
767
809
|
async def _send_formatted_message(payload: str) -> None:
|
|
768
810
|
await bot.send_message(
|
|
@@ -779,6 +821,7 @@ async def reply_large_text(
|
|
|
779
821
|
prepared,
|
|
780
822
|
_send_formatted_message,
|
|
781
823
|
raw_sender=_send_raw_message,
|
|
824
|
+
fallback_payload=fallback_payload,
|
|
782
825
|
)
|
|
783
826
|
|
|
784
827
|
worker_log.info(
|
|
@@ -797,10 +840,12 @@ async def reply_large_text(
|
|
|
797
840
|
f"内容较长,已生成附件 `{attachment_name}`,请下载查看全文。"
|
|
798
841
|
)
|
|
799
842
|
|
|
843
|
+
summary_prepared, summary_fallback = _prepare_model_payload_variants(summary_text)
|
|
800
844
|
delivered_summary = await _send_with_markdown_guard(
|
|
801
|
-
|
|
845
|
+
summary_prepared,
|
|
802
846
|
_send_formatted_message,
|
|
803
847
|
raw_sender=_send_raw_message,
|
|
848
|
+
fallback_payload=summary_fallback,
|
|
804
849
|
)
|
|
805
850
|
|
|
806
851
|
document = BufferedInputFile(text.encode("utf-8"), filename=attachment_name)
|
|
@@ -1514,7 +1559,7 @@ async def _broadcast_worker_keyboard(bot: Bot) -> None:
|
|
|
1514
1559
|
continue
|
|
1515
1560
|
|
|
1516
1561
|
parse_mode = _parse_mode_value()
|
|
1517
|
-
prepared =
|
|
1562
|
+
prepared, fallback_payload = _prepare_model_payload_variants(text)
|
|
1518
1563
|
|
|
1519
1564
|
async def _send_formatted(payload: str) -> None:
|
|
1520
1565
|
await bot.send_message(
|
|
@@ -1537,6 +1582,7 @@ async def _broadcast_worker_keyboard(bot: Bot) -> None:
|
|
|
1537
1582
|
prepared,
|
|
1538
1583
|
_send_formatted,
|
|
1539
1584
|
raw_sender=_send_raw,
|
|
1585
|
+
fallback_payload=fallback_payload,
|
|
1540
1586
|
)
|
|
1541
1587
|
except TelegramForbiddenError as exc:
|
|
1542
1588
|
worker_log.warning("推送任务列表被拒绝:%s", exc, extra={**_session_extra(), "chat": chat_id})
|
|
@@ -2794,13 +2840,32 @@ async def _answer_with_markdown(
|
|
|
2794
2840
|
*,
|
|
2795
2841
|
reply_markup: InlineKeyboardMarkup | ReplyKeyboardMarkup | None = None,
|
|
2796
2842
|
) -> Optional[Message]:
|
|
2797
|
-
prepared =
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2843
|
+
prepared, fallback_payload = _prepare_model_payload_variants(text)
|
|
2844
|
+
sent_message: Optional[Message] = None
|
|
2845
|
+
|
|
2846
|
+
async def _send(payload: str) -> None:
|
|
2847
|
+
nonlocal sent_message
|
|
2848
|
+
sent_message = await message.answer(
|
|
2849
|
+
payload,
|
|
2801
2850
|
parse_mode=_parse_mode_value(),
|
|
2802
2851
|
reply_markup=reply_markup,
|
|
2803
2852
|
)
|
|
2853
|
+
|
|
2854
|
+
async def _send_raw(payload: str) -> None:
|
|
2855
|
+
nonlocal sent_message
|
|
2856
|
+
sent_message = await message.answer(
|
|
2857
|
+
payload,
|
|
2858
|
+
parse_mode=None,
|
|
2859
|
+
reply_markup=reply_markup,
|
|
2860
|
+
)
|
|
2861
|
+
|
|
2862
|
+
try:
|
|
2863
|
+
await _send_with_markdown_guard(
|
|
2864
|
+
prepared,
|
|
2865
|
+
_send,
|
|
2866
|
+
raw_sender=_send_raw,
|
|
2867
|
+
fallback_payload=fallback_payload,
|
|
2868
|
+
)
|
|
2804
2869
|
except TelegramBadRequest as exc:
|
|
2805
2870
|
worker_log.warning(
|
|
2806
2871
|
"发送消息失败:%s",
|
|
@@ -2808,7 +2873,7 @@ async def _answer_with_markdown(
|
|
|
2808
2873
|
extra={"chat": getattr(message.chat, "id", None)},
|
|
2809
2874
|
)
|
|
2810
2875
|
return None
|
|
2811
|
-
return
|
|
2876
|
+
return sent_message
|
|
2812
2877
|
|
|
2813
2878
|
|
|
2814
2879
|
async def _edit_message_with_markdown(
|
|
@@ -2817,11 +2882,27 @@ async def _edit_message_with_markdown(
|
|
|
2817
2882
|
*,
|
|
2818
2883
|
reply_markup: InlineKeyboardMarkup | None = None,
|
|
2819
2884
|
) -> None:
|
|
2820
|
-
prepared =
|
|
2821
|
-
|
|
2885
|
+
prepared, fallback_payload = _prepare_model_payload_variants(text)
|
|
2886
|
+
|
|
2887
|
+
async def _send(payload: str) -> None:
|
|
2888
|
+
await callback.message.edit_text(
|
|
2889
|
+
payload,
|
|
2890
|
+
parse_mode=_parse_mode_value(),
|
|
2891
|
+
reply_markup=reply_markup,
|
|
2892
|
+
)
|
|
2893
|
+
|
|
2894
|
+
async def _send_raw(payload: str) -> None:
|
|
2895
|
+
await callback.message.edit_text(
|
|
2896
|
+
payload,
|
|
2897
|
+
parse_mode=None,
|
|
2898
|
+
reply_markup=reply_markup,
|
|
2899
|
+
)
|
|
2900
|
+
|
|
2901
|
+
await _send_with_markdown_guard(
|
|
2822
2902
|
prepared,
|
|
2823
|
-
|
|
2824
|
-
|
|
2903
|
+
_send,
|
|
2904
|
+
raw_sender=_send_raw,
|
|
2905
|
+
fallback_payload=fallback_payload,
|
|
2825
2906
|
)
|
|
2826
2907
|
|
|
2827
2908
|
|
|
@@ -2833,13 +2914,29 @@ async def _try_edit_message(
|
|
|
2833
2914
|
) -> bool:
|
|
2834
2915
|
if message is None:
|
|
2835
2916
|
return False
|
|
2836
|
-
prepared =
|
|
2837
|
-
|
|
2917
|
+
prepared, fallback_payload = _prepare_model_payload_variants(text)
|
|
2918
|
+
|
|
2919
|
+
async def _send(payload: str) -> None:
|
|
2838
2920
|
await message.edit_text(
|
|
2839
|
-
|
|
2921
|
+
payload,
|
|
2840
2922
|
parse_mode=_parse_mode_value(),
|
|
2841
2923
|
reply_markup=reply_markup,
|
|
2842
2924
|
)
|
|
2925
|
+
|
|
2926
|
+
async def _send_raw(payload: str) -> None:
|
|
2927
|
+
await message.edit_text(
|
|
2928
|
+
payload,
|
|
2929
|
+
parse_mode=None,
|
|
2930
|
+
reply_markup=reply_markup,
|
|
2931
|
+
)
|
|
2932
|
+
|
|
2933
|
+
try:
|
|
2934
|
+
await _send_with_markdown_guard(
|
|
2935
|
+
prepared,
|
|
2936
|
+
_send,
|
|
2937
|
+
raw_sender=_send_raw,
|
|
2938
|
+
fallback_payload=fallback_payload,
|
|
2939
|
+
)
|
|
2843
2940
|
return True
|
|
2844
2941
|
except TelegramBadRequest as exc:
|
|
2845
2942
|
worker_log.info(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
bot.py,sha256=
|
|
1
|
+
bot.py,sha256=RHE0IJcgiuXThExBQs4ZqxYF-Tfqpg3tIC5yYTGLr4o,274363
|
|
2
2
|
logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
|
|
3
3
|
master.py,sha256=ZW4A3Gh0MUKFnfZX-VJ7OCNnBzlEcOWkPKYH92OfyKA,112967
|
|
4
4
|
project_repository.py,sha256=UcthtSGOJK0cTE5bQCneo3xkomRG-kyc1N1QVqxeHIs,17577
|
|
@@ -427,14 +427,14 @@ tasks/fsm.py,sha256=rKXXLEieQQU4r2z_CZUvn1_70FXiZXBBugF40gpe_tQ,1476
|
|
|
427
427
|
tasks/models.py,sha256=N_qqRBo9xMSV0vbn4k6bLBXT8C_dp_oTFUxvdx16ZQM,2459
|
|
428
428
|
tasks/service.py,sha256=w_S_aWiVqRXzXEpimLDsuCCCX2lB5uDkff9aKThBw9c,41916
|
|
429
429
|
telegram_markdown/__init__.py,sha256=bG3H9fWn5GfTqC6xvd49xbVdYWfSFeaX2nefweOYcWY,9757
|
|
430
|
-
vibego_cli/__init__.py,sha256=
|
|
430
|
+
vibego_cli/__init__.py,sha256=n3G3v9lJLz5-SZnbrTB8h6FL6u1LF37J0JJ1pCcB_CM,311
|
|
431
431
|
vibego_cli/__main__.py,sha256=qqTrYmRRLe4361fMzbI3-CqpZ7AhTofIHmfp4ykrrBY,158
|
|
432
432
|
vibego_cli/config.py,sha256=VxkPJMq01tA3h3cOkH-z_tiP7pMgfSGGicRvUnCWkhI,3054
|
|
433
433
|
vibego_cli/deps.py,sha256=1nRXI7Dd-S1hYE8DligzK5fIluQWETRUj4_OKL0DikQ,1419
|
|
434
434
|
vibego_cli/main.py,sha256=X__NXwZnIDIFbdKSTbNyZgZHKcPlN0DQz9sqTI1aQ9E,12158
|
|
435
435
|
vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
|
|
436
|
-
vibego-0.2.
|
|
437
|
-
vibego-0.2.
|
|
438
|
-
vibego-0.2.
|
|
439
|
-
vibego-0.2.
|
|
440
|
-
vibego-0.2.
|
|
436
|
+
vibego-0.2.37.dist-info/METADATA,sha256=U_HSCRWJDsLvWIP2xu86nBcqrDhkJsOESZBJzLDnvSw,10519
|
|
437
|
+
vibego-0.2.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
438
|
+
vibego-0.2.37.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
|
|
439
|
+
vibego-0.2.37.dist-info/top_level.txt,sha256=rWDj9KERtbJL6Lar9Xa0O6dthaFSY_jc1WNpQgUrXCM,87
|
|
440
|
+
vibego-0.2.37.dist-info/RECORD,,
|
vibego_cli/__init__.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|