vibego 0.2.18__py3-none-any.whl → 0.2.19__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 +172 -16
- master.py +2 -1
- {vibego-0.2.18.dist-info → vibego-0.2.19.dist-info}/METADATA +1 -1
- {vibego-0.2.18.dist-info → vibego-0.2.19.dist-info}/RECORD +8 -8
- vibego_cli/__init__.py +1 -1
- {vibego-0.2.18.dist-info → vibego-0.2.19.dist-info}/WHEEL +0 -0
- {vibego-0.2.18.dist-info → vibego-0.2.19.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.18.dist-info → vibego-0.2.19.dist-info}/top_level.txt +0 -0
bot.py
CHANGED
|
@@ -391,19 +391,45 @@ async def _send_with_retry(coro_factory, *, attempts: int = SEND_RETRY_ATTEMPTS)
|
|
|
391
391
|
|
|
392
392
|
|
|
393
393
|
def _escape_markdown_v2(text: str) -> str:
|
|
394
|
-
"""转义 MarkdownV2
|
|
394
|
+
"""转义 MarkdownV2 特殊字符,保护代码块内容。
|
|
395
395
|
|
|
396
396
|
注意:
|
|
397
|
+
- 使用分段处理,保护代码块(```...``` 和 `...`)
|
|
397
398
|
- Text().as_markdown() 会转义所有 MarkdownV2 特殊字符
|
|
398
399
|
- 只移除纯英文单词之间的连字符转义(如 "pre-release")
|
|
399
400
|
- 保留数字、时间戳等其他情况的连字符转义(如 "2025-10-23")
|
|
401
|
+
- 代码块内容不被转义,保持原样
|
|
400
402
|
"""
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
403
|
+
|
|
404
|
+
def _escape_segment(segment: str) -> str:
|
|
405
|
+
"""转义单个文本段落(非代码块)"""
|
|
406
|
+
escaped = Text(segment).as_markdown()
|
|
407
|
+
# 只移除纯英文字母之间的连字符转义
|
|
408
|
+
escaped = re.sub(r"(?<=[a-zA-Z])\\-(?=[a-zA-Z])", "-", escaped)
|
|
409
|
+
# 移除斜杠的转义
|
|
410
|
+
escaped = escaped.replace("\\/", "/")
|
|
411
|
+
return escaped
|
|
412
|
+
|
|
413
|
+
# 分段处理:代码块保持原样,普通文本转义
|
|
414
|
+
pieces: list[str] = []
|
|
415
|
+
last_index = 0
|
|
416
|
+
|
|
417
|
+
for match in CODE_SEGMENT_RE.finditer(text):
|
|
418
|
+
# 处理代码块之前的普通文本
|
|
419
|
+
normal_part = text[last_index:match.start()]
|
|
420
|
+
if normal_part:
|
|
421
|
+
pieces.append(_escape_segment(normal_part))
|
|
422
|
+
|
|
423
|
+
# 代码块保持原样,不转义
|
|
424
|
+
pieces.append(match.group(0))
|
|
425
|
+
last_index = match.end()
|
|
426
|
+
|
|
427
|
+
# 处理最后一段普通文本
|
|
428
|
+
if last_index < len(text):
|
|
429
|
+
remaining = text[last_index:]
|
|
430
|
+
pieces.append(_escape_segment(remaining))
|
|
431
|
+
|
|
432
|
+
return "".join(pieces) if pieces else _escape_segment(text)
|
|
407
433
|
|
|
408
434
|
|
|
409
435
|
LEGACY_DOUBLE_BOLD = re.compile(r"\*\*(.+?)\*\*", re.DOTALL)
|
|
@@ -440,6 +466,131 @@ def _normalize_legacy_markdown(text: str) -> str:
|
|
|
440
466
|
return "".join(pieces)
|
|
441
467
|
|
|
442
468
|
|
|
469
|
+
# MarkdownV2 转义字符模式(用于检测已转义文本)
|
|
470
|
+
_ESCAPED_MARKDOWN_PATTERN = re.compile(
|
|
471
|
+
r"\\[_*\[\]()~`>#+=|{}.!:-]" # 添加了冒号
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# 已转义的代码块模式(转义的反引号)
|
|
475
|
+
_ESCAPED_CODE_BLOCK_PATTERN = re.compile(
|
|
476
|
+
r"(\\\`\\\`\\\`.*?\\\`\\\`\\\`|\\\`[^\\\`]*?\\\`)",
|
|
477
|
+
re.DOTALL
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _is_already_escaped(text: str) -> bool:
|
|
482
|
+
"""检测文本是否已经包含 MarkdownV2 转义字符。
|
|
483
|
+
|
|
484
|
+
通过统计转义字符的出现频率来判断:
|
|
485
|
+
- 如果转义字符数量 >= 文本长度的 3%,认为已被转义(降低阈值)
|
|
486
|
+
- 或者如果有 2 个以上的连续转义模式(如 \*\*),也认为已被转义
|
|
487
|
+
- 或者包含已转义的代码块标记
|
|
488
|
+
"""
|
|
489
|
+
if not text:
|
|
490
|
+
return False
|
|
491
|
+
|
|
492
|
+
# 检查是否有已转义的代码块标记
|
|
493
|
+
if _ESCAPED_CODE_BLOCK_PATTERN.search(text):
|
|
494
|
+
return True
|
|
495
|
+
|
|
496
|
+
matches = _ESCAPED_MARKDOWN_PATTERN.findall(text)
|
|
497
|
+
if not matches:
|
|
498
|
+
return False
|
|
499
|
+
|
|
500
|
+
# 对于短文本,放宽检测条件
|
|
501
|
+
if len(text) < 20:
|
|
502
|
+
# 短文本只要有 2 个以上转义字符就认为已被转义
|
|
503
|
+
if len(matches) >= 2:
|
|
504
|
+
return True
|
|
505
|
+
else:
|
|
506
|
+
# 检查转义字符密度(降低到 3%)
|
|
507
|
+
escape_count = len(matches)
|
|
508
|
+
text_length = len(text)
|
|
509
|
+
density = escape_count / text_length
|
|
510
|
+
|
|
511
|
+
if density >= 0.03: # 3% 以上认为已被转义
|
|
512
|
+
return True
|
|
513
|
+
|
|
514
|
+
# 检查是否有连续转义模式(如 \#\#\# 或 \*\*)
|
|
515
|
+
consecutive_pattern = re.compile(r"(?:\\[_*\[\]()~`>#+=|{}.!:-]){2,}")
|
|
516
|
+
if consecutive_pattern.search(text):
|
|
517
|
+
return True
|
|
518
|
+
|
|
519
|
+
return False
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _unescape_markdown_v2(text: str) -> str:
|
|
523
|
+
"""反转义 MarkdownV2 特殊字符。
|
|
524
|
+
|
|
525
|
+
将 \*, \_, \#, \[, \], \: 等转义字符还原为原始字符。
|
|
526
|
+
"""
|
|
527
|
+
# 移除所有 MarkdownV2 转义的反斜杠
|
|
528
|
+
# 匹配模式:反斜杠 + 特殊字符(添加了冒号)
|
|
529
|
+
return re.sub(r"\\([_*\[\]()~`>#+=|{}.!:-])", r"\1", text)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _unescape_if_already_escaped(text: str) -> str:
|
|
533
|
+
"""智能检测并清理预转义文本,特别保护代码块。
|
|
534
|
+
|
|
535
|
+
改进的分段处理策略:
|
|
536
|
+
1. 先识别已转义的代码块(\`\`\`...\`\`\` 和 \`...\`)
|
|
537
|
+
2. 对这些代码块先反转义边界反引号,变成正常代码块
|
|
538
|
+
3. 然后用正常的 CODE_SEGMENT_RE 识别代码块
|
|
539
|
+
4. 只对非代码块的普通文本进行反转义
|
|
540
|
+
5. 代码块内容保持转义状态(因为是代码本身)
|
|
541
|
+
6. 重新组合所有段落
|
|
542
|
+
|
|
543
|
+
Args:
|
|
544
|
+
text: 待处理的文本
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
处理后的文本(如未检测到预转义,返回原文本)
|
|
548
|
+
"""
|
|
549
|
+
if not text:
|
|
550
|
+
return text
|
|
551
|
+
|
|
552
|
+
# 快速检测:如果没有任何转义字符,直接返回
|
|
553
|
+
if not _is_already_escaped(text):
|
|
554
|
+
return text
|
|
555
|
+
|
|
556
|
+
# 第一步:处理已转义的代码块,将边界反引号反转义
|
|
557
|
+
# 这样后续可以用正常的 CODE_SEGMENT_RE 识别它们
|
|
558
|
+
processed = text
|
|
559
|
+
|
|
560
|
+
# 先标记所有已转义的代码块,用占位符替换
|
|
561
|
+
code_blocks: list[str] = []
|
|
562
|
+
|
|
563
|
+
def _preserve_code_block(match: re.Match[str]) -> str:
|
|
564
|
+
"""保存代码块并返回占位符"""
|
|
565
|
+
block = match.group(0)
|
|
566
|
+
# 代码块边界的反引号需要反转义,但内容保持不变
|
|
567
|
+
# 例如:\`\`\`bash\npython -m vibego\_cli\`\`\`
|
|
568
|
+
# -> ```bash\npython -m vibego\_cli```
|
|
569
|
+
if block.startswith(r"\`\`\`"):
|
|
570
|
+
# 多行代码块
|
|
571
|
+
unescaped_block = block.replace(r"\`", "`", 6) # 只替换前后各3个反引号
|
|
572
|
+
else:
|
|
573
|
+
# 单行代码块
|
|
574
|
+
unescaped_block = block.replace(r"\`", "`", 2) # 只替换前后各1个反引号
|
|
575
|
+
|
|
576
|
+
placeholder = f"__CODE_BLOCK_{len(code_blocks)}__"
|
|
577
|
+
code_blocks.append(unescaped_block)
|
|
578
|
+
return placeholder
|
|
579
|
+
|
|
580
|
+
processed = _ESCAPED_CODE_BLOCK_PATTERN.sub(_preserve_code_block, processed)
|
|
581
|
+
|
|
582
|
+
# 第二步:对非代码块部分进行反转义
|
|
583
|
+
if _is_already_escaped(processed):
|
|
584
|
+
processed = _unescape_markdown_v2(processed)
|
|
585
|
+
|
|
586
|
+
# 第三步:恢复代码块
|
|
587
|
+
for i, block in enumerate(code_blocks):
|
|
588
|
+
placeholder = f"__CODE_BLOCK_{i}__"
|
|
589
|
+
processed = processed.replace(placeholder, block)
|
|
590
|
+
|
|
591
|
+
return processed
|
|
592
|
+
|
|
593
|
+
|
|
443
594
|
def _prepare_model_payload(text: str) -> str:
|
|
444
595
|
if _IS_MARKDOWN_V2:
|
|
445
596
|
return _escape_markdown_v2(text)
|
|
@@ -1994,11 +2145,12 @@ def _build_status_filter_row(current_status: Optional[str], limit: int) -> list[
|
|
|
1994
2145
|
def _format_task_list_entry(task: TaskRecord) -> str:
|
|
1995
2146
|
indent = " " * max(task.depth, 0)
|
|
1996
2147
|
title_raw = (task.title or "").strip()
|
|
1997
|
-
#
|
|
2148
|
+
# 修复:智能清理预转义文本
|
|
1998
2149
|
if not title_raw:
|
|
1999
2150
|
title = "-"
|
|
2000
2151
|
elif _IS_MARKDOWN_V2:
|
|
2001
|
-
|
|
2152
|
+
# 智能清理预转义文本(保护代码块)
|
|
2153
|
+
title = _unescape_if_already_escaped(title_raw)
|
|
2002
2154
|
else:
|
|
2003
2155
|
title = _escape_markdown_text(title_raw)
|
|
2004
2156
|
type_icon = TASK_TYPE_EMOJIS.get(task.task_type)
|
|
@@ -2048,11 +2200,13 @@ def _format_task_detail(
|
|
|
2048
2200
|
*,
|
|
2049
2201
|
notes: Sequence[TaskNoteRecord],
|
|
2050
2202
|
) -> str:
|
|
2051
|
-
#
|
|
2052
|
-
# MarkdownV2
|
|
2203
|
+
# 修复:智能处理预转义文本
|
|
2204
|
+
# - MarkdownV2 模式:先清理可能的预转义,再由 _prepare_model_payload() 统一处理
|
|
2205
|
+
# - 其他模式:手动转义
|
|
2053
2206
|
title_raw = (task.title or "").strip()
|
|
2054
2207
|
if _IS_MARKDOWN_V2:
|
|
2055
|
-
|
|
2208
|
+
# 智能清理预转义文本(保护代码块)
|
|
2209
|
+
title_text = _unescape_if_already_escaped(title_raw) if title_raw else "-"
|
|
2056
2210
|
else:
|
|
2057
2211
|
title_text = _escape_markdown_text(title_raw) if title_raw else "-"
|
|
2058
2212
|
|
|
@@ -2065,10 +2219,11 @@ def _format_task_detail(
|
|
|
2065
2219
|
f"📂 类型:{_format_task_type(task.task_type)}",
|
|
2066
2220
|
]
|
|
2067
2221
|
|
|
2068
|
-
#
|
|
2222
|
+
# 修复:描述字段智能清理预转义
|
|
2069
2223
|
description_raw = task.description or "暂无"
|
|
2070
2224
|
if _IS_MARKDOWN_V2:
|
|
2071
|
-
|
|
2225
|
+
# 智能清理预转义文本(保护代码块)
|
|
2226
|
+
description_text = _unescape_if_already_escaped(description_raw)
|
|
2072
2227
|
else:
|
|
2073
2228
|
description_text = _escape_markdown_text(description_raw)
|
|
2074
2229
|
|
|
@@ -2076,10 +2231,11 @@ def _format_task_detail(
|
|
|
2076
2231
|
lines.append(f"📅 创建时间:{_format_local_time(task.created_at)}")
|
|
2077
2232
|
lines.append(f"🔁 更新时间:{_format_local_time(task.updated_at)}")
|
|
2078
2233
|
|
|
2079
|
-
# 修复:父任务ID
|
|
2234
|
+
# 修复:父任务ID字段智能清理预转义
|
|
2080
2235
|
if task.parent_id:
|
|
2081
2236
|
if _IS_MARKDOWN_V2:
|
|
2082
|
-
|
|
2237
|
+
# 智能清理预转义文本(保护代码块)
|
|
2238
|
+
parent_text = _unescape_if_already_escaped(task.parent_id)
|
|
2083
2239
|
else:
|
|
2084
2240
|
parent_text = _escape_markdown_text(task.parent_id)
|
|
2085
2241
|
lines.append(f"👪 父任务:{parent_text}")
|
master.py
CHANGED
|
@@ -54,6 +54,7 @@ from aiogram.fsm.storage.memory import MemoryStorage
|
|
|
54
54
|
from logging_setup import create_logger
|
|
55
55
|
from project_repository import ProjectRepository, ProjectRecord
|
|
56
56
|
from tasks.fsm import ProjectDeleteStates
|
|
57
|
+
from vibego_cli import __version__
|
|
57
58
|
|
|
58
59
|
ROOT_DIR = Path(__file__).resolve().parent
|
|
59
60
|
CONFIG_PATH = Path(os.environ.get("MASTER_PROJECTS_PATH", ROOT_DIR / "config/projects.json"))
|
|
@@ -1792,7 +1793,7 @@ async def cmd_start(message: Message) -> None:
|
|
|
1792
1793
|
return
|
|
1793
1794
|
manager.refresh_state()
|
|
1794
1795
|
await message.answer(
|
|
1795
|
-
"Master bot
|
|
1796
|
+
f"Master bot 已启动(v{__version__})。\n"
|
|
1796
1797
|
f"已登记项目: {len(manager.configs)} 个。\n"
|
|
1797
1798
|
"使用 /projects 查看状态,/run 或 /stop 控制 worker。",
|
|
1798
1799
|
reply_markup=_build_master_main_keyboard(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
bot.py,sha256=
|
|
1
|
+
bot.py,sha256=aK68QaITXzOoUStLqejg8L6Bd3LuAqNLOw6R3-7SfYw,269638
|
|
2
2
|
logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
|
|
3
|
-
master.py,sha256=
|
|
3
|
+
master.py,sha256=qw6ZxItpSr6C5YzjVDOGm4jZpnGYLZndsJ-lmJrzGcM,106553
|
|
4
4
|
project_repository.py,sha256=UcthtSGOJK0cTE5bQCneo3xkomRG-kyc1N1QVqxeHIs,17577
|
|
5
5
|
scripts/__init__.py,sha256=LVrXUkvWKoc6Sb47X5G0gbIxu5aJ2ARW-qJ14vwi5vM,65
|
|
6
6
|
scripts/bump_version.sh,sha256=a4uB8V8Y5LPsoqTCdzQKsEE8HhwpBmqRaQInG52LDig,4089
|
|
@@ -425,14 +425,14 @@ tasks/constants.py,sha256=tS1kZxBIUm3JJUMHm25XI-KHNUZl5NhbbuzjzL_rF-c,299
|
|
|
425
425
|
tasks/fsm.py,sha256=rKXXLEieQQU4r2z_CZUvn1_70FXiZXBBugF40gpe_tQ,1476
|
|
426
426
|
tasks/models.py,sha256=N_qqRBo9xMSV0vbn4k6bLBXT8C_dp_oTFUxvdx16ZQM,2459
|
|
427
427
|
tasks/service.py,sha256=w_S_aWiVqRXzXEpimLDsuCCCX2lB5uDkff9aKThBw9c,41916
|
|
428
|
-
vibego_cli/__init__.py,sha256=
|
|
428
|
+
vibego_cli/__init__.py,sha256=PbHZ3j0Ku7MxszOczRhZnNfiggYU_AB2P91m7bMQqCw,311
|
|
429
429
|
vibego_cli/__main__.py,sha256=qqTrYmRRLe4361fMzbI3-CqpZ7AhTofIHmfp4ykrrBY,158
|
|
430
430
|
vibego_cli/config.py,sha256=33WSORCfUIxrDtgASPEbVqVLBVNHh-RSFLpNy7tfc0s,2992
|
|
431
431
|
vibego_cli/deps.py,sha256=1nRXI7Dd-S1hYE8DligzK5fIluQWETRUj4_OKL0DikQ,1419
|
|
432
432
|
vibego_cli/main.py,sha256=e2W5Pb9U9rfmF-jNX9uIA3222lhM0GgcvSdFTDBZd2s,12086
|
|
433
433
|
vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
|
|
434
|
-
vibego-0.2.
|
|
435
|
-
vibego-0.2.
|
|
436
|
-
vibego-0.2.
|
|
437
|
-
vibego-0.2.
|
|
438
|
-
vibego-0.2.
|
|
434
|
+
vibego-0.2.19.dist-info/METADATA,sha256=EcNsx_kVnc0gZiqMdbpIHRDYcIFu09gERPQubTVW-mY,10475
|
|
435
|
+
vibego-0.2.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
436
|
+
vibego-0.2.19.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
|
|
437
|
+
vibego-0.2.19.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
|
|
438
|
+
vibego-0.2.19.dist-info/RECORD,,
|
vibego_cli/__init__.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|