55
66import secrets
77import time
8- import unicodedata
98from datetime import datetime , timezone
109from typing import Dict , Any , Optional
1110from collections import defaultdict
@@ -73,7 +72,6 @@ def __init__(self, config: Config, data_manager: DataManager, application=None):
7372 self .MAX_DESCRIPTION_LENGTH = 20
7473 self .MAX_ADDS_PER_HOUR = 50
7574 self .MAX_DETAIL_LINES = 4
76- self .MAX_DETAIL_LINE_LENGTH = 56
7775 self .STATE_TTL = 1800
7876 self .ACTION_TTL = 900
7977 self .MAX_USER_STATES = 4096
@@ -565,7 +563,7 @@ def _raw_telegram_identity(user) -> str:
565563
566564 def _format_telegram_identity (self , user ) -> str :
567565 """Format a Telegram identity for Markdown-visible messages."""
568- identity = self ._truncate_display (self ._raw_telegram_identity (user ), 40 )
566+ identity = self ._single_line_display (self ._raw_telegram_identity (user ))
569567 identity = self .escape_markdown (identity )
570568 if getattr (user , "username" , None ):
571569 return f"@{ identity } "
@@ -610,45 +608,18 @@ def _repo_and_home_keyboard(self) -> InlineKeyboardMarkup:
610608 )
611609
612610 @staticmethod
613- def _display_width (value : object ) -> int :
614- width = 0
615- for char in str (value ):
616- if unicodedata .combining (char ):
617- continue
618- width += 2 if unicodedata .east_asian_width (char ) in {"W" , "F" } else 1
619- return width
620-
621- @classmethod
622- def _truncate_display (cls , value : object , max_width : int = 56 ) -> str :
623- """Collapse and bound dynamic text by its approximate rendered width."""
624- text = " " .join (str (value ).split ())
625- if cls ._display_width (text ) <= max_width :
626- return text
627- visible = []
628- width = 0
629- for char in text :
630- char_width = (
631- 0
632- if unicodedata .combining (char )
633- else 2
634- if unicodedata .east_asian_width (char ) in {"W" , "F" }
635- else 1
636- )
637- if width + char_width > max_width - 1 :
638- break
639- visible .append (char )
640- width += char_width
641- return "" .join (visible ).rstrip () + "…"
611+ def _single_line_display (value : object ) -> str :
612+ """Normalize dynamic text without discarding visible content."""
613+ return " " .join (str (value ).split ())
642614
643615 def _format_rule_matches (self , matches : list , limit : Optional [int ] = None ) -> str :
644- """Bound dynamic GitHub matches so Telegram messages remain below limits ."""
616+ """Format dynamic GitHub matches as complete, independently wrapped rows ."""
645617 if not matches :
646618 return ""
647619 limit = limit or getattr (self , "MAX_DETAIL_LINES" , 4 )
648- line_limit = getattr (self , "MAX_DETAIL_LINE_LENGTH" , 56 )
649620 lines = []
650621 for match in matches [:limit ]:
651- rule = self ._truncate_display (match .get ("rule" , "" ), line_limit - 12 )
622+ rule = self ._single_line_display (match .get ("rule" , "" ))
652623 lines .append (f"• 第 { match .get ('line' , '?' )} 行:{ self .escape_markdown (rule )} " )
653624 remaining = len (matches ) - limit
654625 if remaining > 0 :
@@ -659,7 +630,7 @@ def _format_rule_matches(self, matches: list, limit: Optional[int] = None) -> st
659630 def _format_value_list (values : list , limit : int = 3 ) -> str :
660631 """Format network values one-per-line for narrow Telegram clients."""
661632 visible = [
662- HandlerManager ._truncate_display (value , 56 )
633+ HandlerManager ._single_line_display (value )
663634 for value in list (values or [])[:limit ]
664635 ]
665636 remaining = len (values or []) - len (visible )
@@ -704,9 +675,7 @@ def _build_add_review_text(self, domain: str, check_result: dict, user) -> tuple
704675 [
705676 "" ,
706677 "💡 *判断依据*" ,
707- self .escape_markdown (
708- self ._truncate_display (recommendation , 56 )
709- ),
678+ self .escape_markdown (self ._single_line_display (recommendation )),
710679 ]
711680 )
712681 if not should_reject :
@@ -880,7 +849,7 @@ async def _display_message_result(
880849
881850 def _build_main_menu_text (self , username : str ) -> str :
882851 """构建主菜单文案"""
883- username = self .escape_markdown (self ._truncate_display (username , 24 ))
852+ username = self .escape_markdown (self ._single_line_display (username ))
884853 repo = str (self .config .GITHUB_REPO ).strip ()
885854 repo_label = self .escape_markdown (repo )
886855 return "\n " .join (
@@ -1024,9 +993,7 @@ def _format_detail_lines(
1024993 limit = limit or self .MAX_DETAIL_LINES
1025994 lines = []
1026995 for detail in details [:limit ]:
1027- detail = self ._truncate_display (
1028- detail , self .MAX_DETAIL_LINE_LENGTH - 2
1029- )
996+ detail = self ._single_line_display (detail )
1030997 lines .append (f"• { self .escape_markdown (detail )} " )
1031998
1032999 remaining = len (details ) - limit
@@ -1048,7 +1015,7 @@ def _build_query_summary_text(
10481015 github_exists = bool (github_result .get ("exists" ))
10491016 check_failed = "error" in check_result
10501017 matches = github_result .get ("matches" , [])
1051- visible_domain = self ._truncate_display (domain , 56 )
1018+ visible_domain = self ._single_line_display (domain )
10521019
10531020 if github_unavailable :
10541021 github_status = "暂时无法读取"
@@ -1109,8 +1076,13 @@ def _build_query_summary_text(
11091076 and not github_exists
11101077 and not in_geosite
11111078 ):
1112- visible = self ._truncate_display (recommendation , 48 )
1113- lines .append (f"💡 综合判断:{ self .escape_markdown (visible )} " )
1079+ lines .extend (
1080+ [
1081+ "" ,
1082+ "💡 *综合判断*" ,
1083+ self .escape_markdown (self ._single_line_display (recommendation )),
1084+ ]
1085+ )
11141086 return "\n " .join (lines )
11151087
11161088 def _build_query_detail_text (
@@ -1120,7 +1092,7 @@ def _build_query_detail_text(
11201092 check_result : dict ,
11211093 ) -> str :
11221094 """Build a bounded technical page without repeating the summary."""
1123- visible_domain = self ._truncate_display (domain , 56 )
1095+ visible_domain = self ._single_line_display (domain )
11241096 lines = ["🔎 *技术详情*" , "" , f"`{ visible_domain } `" ]
11251097 github_unavailable = bool (github_result .get ("error" ))
11261098 matches = github_result .get ("matches" , [])
0 commit comments