@@ -10,6 +10,7 @@ interface SendMessageWithMarkdownFallbackParams {
1010 api : SendMessageApi ;
1111 chatId : Parameters < SendMessageApi [ "sendMessage" ] > [ 0 ] ;
1212 text : string ;
13+ rawFallbackText ?: string ;
1314 options ?: TelegramSendMessageOptions ;
1415 parseMode ?: "Markdown" | "MarkdownV2" ;
1516}
@@ -19,6 +20,7 @@ interface EditMessageWithMarkdownFallbackParams {
1920 chatId : Parameters < EditMessageApi [ "editMessageText" ] > [ 0 ] ;
2021 messageId : Parameters < EditMessageApi [ "editMessageText" ] > [ 1 ] ;
2122 text : string ;
23+ rawFallbackText ?: string ;
2224 options ?: TelegramEditMessageOptions ;
2325 parseMode ?: "Markdown" | "MarkdownV2" ;
2426}
@@ -31,10 +33,56 @@ const MARKDOWN_PARSE_ERROR_MARKERS = [
3133 "bad request: can't parse" ,
3234] ;
3335
34- const MARKDOWN_V2_RESERVED_CHARS = / ( [ _ \* \[ \] \( \) ~ ` > # + \- = | { } . ! \\ ] ) / g;
36+ const MARKDOWN_V2_RESERVED_CHARS = new Set ( [
37+ "_" ,
38+ "*" ,
39+ "[" ,
40+ "]" ,
41+ "(" ,
42+ ")" ,
43+ "~" ,
44+ "`" ,
45+ ">" ,
46+ "#" ,
47+ "+" ,
48+ "-" ,
49+ "=" ,
50+ "|" ,
51+ "{" ,
52+ "}" ,
53+ "." ,
54+ "!" ,
55+ "\\" ,
56+ ] ) ;
57+ const MARKDOWN_V2_ESCAPED_CHAR = / \\ ( [ _ \* \[ \] \( \) ~ ` > # + \- = | { } . ! \\ ] ) / g;
3558
3659function escapeTelegramMarkdownV2 ( text : string ) : string {
37- return text . replace ( MARKDOWN_V2_RESERVED_CHARS , "\\$1" ) ;
60+ let result = "" ;
61+ let trailingBackslashes = 0 ;
62+
63+ for ( const char of text ) {
64+ if ( char === "\\" ) {
65+ result += char ;
66+ trailingBackslashes += 1 ;
67+ continue ;
68+ }
69+
70+ const isEscaped = trailingBackslashes % 2 === 1 ;
71+ trailingBackslashes = 0 ;
72+
73+ if ( MARKDOWN_V2_RESERVED_CHARS . has ( char ) && ! isEscaped ) {
74+ result += `\\${ char } ` ;
75+ continue ;
76+ }
77+
78+ result += char ;
79+ }
80+
81+ return result ;
82+ }
83+
84+ function unescapeTelegramMarkdownV2 ( text : string ) : string {
85+ return text . replace ( MARKDOWN_V2_ESCAPED_CHAR , "$1" ) ;
3886}
3987
4088function stripMarkdownFormattingOptions <
@@ -100,6 +148,7 @@ export async function sendMessageWithMarkdownFallback({
100148 api,
101149 chatId,
102150 text,
151+ rawFallbackText,
103152 options,
104153 parseMode,
105154} : SendMessageWithMarkdownFallbackParams ) : Promise <
@@ -114,6 +163,9 @@ export async function sendMessageWithMarkdownFallback({
114163 parse_mode : parseMode ,
115164 } ;
116165
166+ const fallbackText =
167+ rawFallbackText ?? ( parseMode === "MarkdownV2" ? unescapeTelegramMarkdownV2 ( text ) : text ) ;
168+
117169 try {
118170 return await api . sendMessage ( chatId , text , markdownOptions ) ;
119171 } catch ( error ) {
@@ -140,13 +192,13 @@ export async function sendMessageWithMarkdownFallback({
140192 "[Bot] Escaped Markdown parse failed, retrying assistant message in raw mode" ,
141193 escapedError ,
142194 ) ;
143- return api . sendMessage ( chatId , text , stripMarkdownFormattingOptions ( options ) ) ;
195+ return api . sendMessage ( chatId , fallbackText , stripMarkdownFormattingOptions ( options ) ) ;
144196 }
145197 }
146198 }
147199
148200 logger . warn ( "[Bot] Markdown parse failed, retrying assistant message in raw mode" , error ) ;
149- return api . sendMessage ( chatId , text , stripMarkdownFormattingOptions ( options ) ) ;
201+ return api . sendMessage ( chatId , fallbackText , stripMarkdownFormattingOptions ( options ) ) ;
150202 }
151203}
152204
@@ -155,6 +207,7 @@ export async function editMessageWithMarkdownFallback({
155207 chatId,
156208 messageId,
157209 text,
210+ rawFallbackText,
158211 options,
159212 parseMode,
160213} : EditMessageWithMarkdownFallbackParams ) : Promise <
@@ -169,6 +222,9 @@ export async function editMessageWithMarkdownFallback({
169222 parse_mode : parseMode ,
170223 } ;
171224
225+ const fallbackText =
226+ rawFallbackText ?? ( parseMode === "MarkdownV2" ? unescapeTelegramMarkdownV2 ( text ) : text ) ;
227+
172228 try {
173229 return await api . editMessageText ( chatId , messageId , text , markdownOptions ) ;
174230 } catch ( error ) {
@@ -198,14 +254,19 @@ export async function editMessageWithMarkdownFallback({
198254 return api . editMessageText (
199255 chatId ,
200256 messageId ,
201- text ,
257+ fallbackText ,
202258 stripMarkdownFormattingOptions ( options ) ,
203259 ) ;
204260 }
205261 }
206262 }
207263
208264 logger . warn ( "[Bot] Markdown parse failed, retrying edited message in raw mode" , error ) ;
209- return api . editMessageText ( chatId , messageId , text , stripMarkdownFormattingOptions ( options ) ) ;
265+ return api . editMessageText (
266+ chatId ,
267+ messageId ,
268+ fallbackText ,
269+ stripMarkdownFormattingOptions ( options ) ,
270+ ) ;
210271 }
211272}
0 commit comments