vibego 0.2.16__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 +178 -14
- master.py +2 -1
- {vibego-0.2.16.dist-info → vibego-0.2.19.dist-info}/METADATA +1 -1
- {vibego-0.2.16.dist-info → vibego-0.2.19.dist-info}/RECORD +8 -8
- vibego_cli/__init__.py +1 -1
- {vibego-0.2.16.dist-info → vibego-0.2.19.dist-info}/WHEEL +0 -0
- {vibego-0.2.16.dist-info → vibego-0.2.19.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.16.dist-info → vibego-0.2.19.dist-info}/top_level.txt +0 -0
bot.py
CHANGED
|
@@ -391,11 +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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
394
|
+
"""转义 MarkdownV2 特殊字符,保护代码块内容。
|
|
395
|
+
|
|
396
|
+
注意:
|
|
397
|
+
- 使用分段处理,保护代码块(```...``` 和 `...`)
|
|
398
|
+
- Text().as_markdown() 会转义所有 MarkdownV2 特殊字符
|
|
399
|
+
- 只移除纯英文单词之间的连字符转义(如 "pre-release")
|
|
400
|
+
- 保留数字、时间戳等其他情况的连字符转义(如 "2025-10-23")
|
|
401
|
+
- 代码块内容不被转义,保持原样
|
|
402
|
+
"""
|
|
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)
|
|
399
433
|
|
|
400
434
|
|
|
401
435
|
LEGACY_DOUBLE_BOLD = re.compile(r"\*\*(.+?)\*\*", re.DOTALL)
|
|
@@ -432,6 +466,131 @@ def _normalize_legacy_markdown(text: str) -> str:
|
|
|
432
466
|
return "".join(pieces)
|
|
433
467
|
|
|
434
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
|
+
|
|
435
594
|
def _prepare_model_payload(text: str) -> str:
|
|
436
595
|
if _IS_MARKDOWN_V2:
|
|
437
596
|
return _escape_markdown_v2(text)
|
|
@@ -1986,11 +2145,12 @@ def _build_status_filter_row(current_status: Optional[str], limit: int) -> list[
|
|
|
1986
2145
|
def _format_task_list_entry(task: TaskRecord) -> str:
|
|
1987
2146
|
indent = " " * max(task.depth, 0)
|
|
1988
2147
|
title_raw = (task.title or "").strip()
|
|
1989
|
-
#
|
|
2148
|
+
# 修复:智能清理预转义文本
|
|
1990
2149
|
if not title_raw:
|
|
1991
2150
|
title = "-"
|
|
1992
2151
|
elif _IS_MARKDOWN_V2:
|
|
1993
|
-
|
|
2152
|
+
# 智能清理预转义文本(保护代码块)
|
|
2153
|
+
title = _unescape_if_already_escaped(title_raw)
|
|
1994
2154
|
else:
|
|
1995
2155
|
title = _escape_markdown_text(title_raw)
|
|
1996
2156
|
type_icon = TASK_TYPE_EMOJIS.get(task.task_type)
|
|
@@ -2040,11 +2200,13 @@ def _format_task_detail(
|
|
|
2040
2200
|
*,
|
|
2041
2201
|
notes: Sequence[TaskNoteRecord],
|
|
2042
2202
|
) -> str:
|
|
2043
|
-
#
|
|
2044
|
-
# MarkdownV2
|
|
2203
|
+
# 修复:智能处理预转义文本
|
|
2204
|
+
# - MarkdownV2 模式:先清理可能的预转义,再由 _prepare_model_payload() 统一处理
|
|
2205
|
+
# - 其他模式:手动转义
|
|
2045
2206
|
title_raw = (task.title or "").strip()
|
|
2046
2207
|
if _IS_MARKDOWN_V2:
|
|
2047
|
-
|
|
2208
|
+
# 智能清理预转义文本(保护代码块)
|
|
2209
|
+
title_text = _unescape_if_already_escaped(title_raw) if title_raw else "-"
|
|
2048
2210
|
else:
|
|
2049
2211
|
title_text = _escape_markdown_text(title_raw) if title_raw else "-"
|
|
2050
2212
|
|
|
@@ -2057,10 +2219,11 @@ def _format_task_detail(
|
|
|
2057
2219
|
f"📂 类型:{_format_task_type(task.task_type)}",
|
|
2058
2220
|
]
|
|
2059
2221
|
|
|
2060
|
-
#
|
|
2222
|
+
# 修复:描述字段智能清理预转义
|
|
2061
2223
|
description_raw = task.description or "暂无"
|
|
2062
2224
|
if _IS_MARKDOWN_V2:
|
|
2063
|
-
|
|
2225
|
+
# 智能清理预转义文本(保护代码块)
|
|
2226
|
+
description_text = _unescape_if_already_escaped(description_raw)
|
|
2064
2227
|
else:
|
|
2065
2228
|
description_text = _escape_markdown_text(description_raw)
|
|
2066
2229
|
|
|
@@ -2068,10 +2231,11 @@ def _format_task_detail(
|
|
|
2068
2231
|
lines.append(f"📅 创建时间:{_format_local_time(task.created_at)}")
|
|
2069
2232
|
lines.append(f"🔁 更新时间:{_format_local_time(task.updated_at)}")
|
|
2070
2233
|
|
|
2071
|
-
# 修复:父任务ID
|
|
2234
|
+
# 修复:父任务ID字段智能清理预转义
|
|
2072
2235
|
if task.parent_id:
|
|
2073
2236
|
if _IS_MARKDOWN_V2:
|
|
2074
|
-
|
|
2237
|
+
# 智能清理预转义文本(保护代码块)
|
|
2238
|
+
parent_text = _unescape_if_already_escaped(task.parent_id)
|
|
2075
2239
|
else:
|
|
2076
2240
|
parent_text = _escape_markdown_text(task.parent_id)
|
|
2077
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
|