bot.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. // Package tgbotapi has functions and types used for interacting with
  2. // the Telegram Bot API.
  3. package tgbotapi
  4. import (
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "mime/multipart"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. "time"
  14. )
  15. // HTTPClient is the type needed for the bot to perform HTTP requests.
  16. type HTTPClient interface {
  17. Do(req *http.Request) (*http.Response, error)
  18. }
  19. // BotAPI allows you to interact with the Telegram Bot API.
  20. type BotAPI struct {
  21. Token string `json:"token"`
  22. Debug bool `json:"debug"`
  23. Buffer int `json:"buffer"`
  24. Self User `json:"-"`
  25. Client HTTPClient `json:"-"`
  26. shutdownChannel chan interface{}
  27. apiEndpoint string
  28. }
  29. // NewBotAPI creates a new BotAPI instance.
  30. //
  31. // It requires a token, provided by @BotFather on Telegram.
  32. func NewBotAPI(token string) (*BotAPI, error) {
  33. return NewBotAPIWithClient(token, APIEndpoint, &http.Client{})
  34. }
  35. // NewBotAPIWithAPIEndpoint creates a new BotAPI instance
  36. // and allows you to pass API endpoint.
  37. //
  38. // It requires a token, provided by @BotFather on Telegram and API endpoint.
  39. func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) {
  40. return NewBotAPIWithClient(token, apiEndpoint, &http.Client{})
  41. }
  42. // NewBotAPIWithClient creates a new BotAPI instance
  43. // and allows you to pass a http.Client.
  44. //
  45. // It requires a token, provided by @BotFather on Telegram and API endpoint.
  46. func NewBotAPIWithClient(token, apiEndpoint string, client HTTPClient) (*BotAPI, error) {
  47. bot := &BotAPI{
  48. Token: token,
  49. Client: client,
  50. Buffer: 100,
  51. shutdownChannel: make(chan interface{}),
  52. apiEndpoint: apiEndpoint,
  53. }
  54. self, err := bot.GetMe()
  55. if err != nil {
  56. return nil, err
  57. }
  58. bot.Self = self
  59. return bot, nil
  60. }
  61. // SetAPIEndpoint changes the Telegram Bot API endpoint used by the instance.
  62. func (bot *BotAPI) SetAPIEndpoint(apiEndpoint string) {
  63. bot.apiEndpoint = apiEndpoint
  64. }
  65. func buildParams(in Params) url.Values {
  66. if in == nil {
  67. return url.Values{}
  68. }
  69. out := url.Values{}
  70. for key, value := range in {
  71. out.Set(key, value)
  72. }
  73. return out
  74. }
  75. // MakeRequest makes a request to a specific endpoint with our token.
  76. func (bot *BotAPI) MakeRequest(endpoint string, params Params) (*APIResponse, error) {
  77. if bot.Debug {
  78. log.Printf("Endpoint: %s, params: %v\n", endpoint, params)
  79. }
  80. method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
  81. values := buildParams(params)
  82. req, err := http.NewRequest("POST", method, strings.NewReader(values.Encode()))
  83. if err != nil {
  84. return &APIResponse{}, err
  85. }
  86. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  87. resp, err := bot.Client.Do(req)
  88. if err != nil {
  89. return nil, err
  90. }
  91. defer resp.Body.Close()
  92. var apiResp APIResponse
  93. bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
  94. if err != nil {
  95. return &apiResp, err
  96. }
  97. if bot.Debug {
  98. log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes))
  99. }
  100. if !apiResp.Ok {
  101. var parameters ResponseParameters
  102. if apiResp.Parameters != nil {
  103. parameters = *apiResp.Parameters
  104. }
  105. return &apiResp, &Error{
  106. Code: apiResp.ErrorCode,
  107. Message: apiResp.Description,
  108. ResponseParameters: parameters,
  109. }
  110. }
  111. return &apiResp, nil
  112. }
  113. // decodeAPIResponse decode response and return slice of bytes if debug enabled.
  114. // If debug disabled, just decode http.Response.Body stream to APIResponse struct
  115. // for efficient memory usage
  116. func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) ([]byte, error) {
  117. if !bot.Debug {
  118. dec := json.NewDecoder(responseBody)
  119. err := dec.Decode(resp)
  120. return nil, err
  121. }
  122. // if debug, read response body
  123. data, err := io.ReadAll(responseBody)
  124. if err != nil {
  125. return nil, err
  126. }
  127. err = json.Unmarshal(data, resp)
  128. if err != nil {
  129. return nil, err
  130. }
  131. return data, nil
  132. }
  133. // UploadFiles makes a request to the API with files.
  134. func (bot *BotAPI) UploadFiles(endpoint string, params Params, files []RequestFile) (*APIResponse, error) {
  135. r, w := io.Pipe()
  136. m := multipart.NewWriter(w)
  137. // This code modified from the very helpful @HirbodBehnam
  138. // https://github.com/go-telegram-bot-api/telegram-bot-api/issues/354#issuecomment-663856473
  139. go func() {
  140. defer w.Close()
  141. defer m.Close()
  142. for field, value := range params {
  143. if err := m.WriteField(field, value); err != nil {
  144. w.CloseWithError(err)
  145. return
  146. }
  147. }
  148. for _, file := range files {
  149. if file.Data.NeedsUpload() {
  150. name, reader, err := file.Data.UploadData()
  151. if err != nil {
  152. w.CloseWithError(err)
  153. return
  154. }
  155. part, err := m.CreateFormFile(file.Name, name)
  156. if err != nil {
  157. w.CloseWithError(err)
  158. return
  159. }
  160. if _, err := io.Copy(part, reader); err != nil {
  161. w.CloseWithError(err)
  162. return
  163. }
  164. if closer, ok := reader.(io.ReadCloser); ok {
  165. if err = closer.Close(); err != nil {
  166. w.CloseWithError(err)
  167. return
  168. }
  169. }
  170. } else {
  171. value := file.Data.SendData()
  172. if err := m.WriteField(file.Name, value); err != nil {
  173. w.CloseWithError(err)
  174. return
  175. }
  176. }
  177. }
  178. }()
  179. if bot.Debug {
  180. log.Printf("Endpoint: %s, params: %v, with %d files\n", endpoint, params, len(files))
  181. }
  182. method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
  183. req, err := http.NewRequest("POST", method, r)
  184. if err != nil {
  185. return nil, err
  186. }
  187. req.Header.Set("Content-Type", m.FormDataContentType())
  188. resp, err := bot.Client.Do(req)
  189. if err != nil {
  190. return nil, err
  191. }
  192. defer resp.Body.Close()
  193. var apiResp APIResponse
  194. bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
  195. if err != nil {
  196. return &apiResp, err
  197. }
  198. if bot.Debug {
  199. log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes))
  200. }
  201. if !apiResp.Ok {
  202. var parameters ResponseParameters
  203. if apiResp.Parameters != nil {
  204. parameters = *apiResp.Parameters
  205. }
  206. return &apiResp, &Error{
  207. Message: apiResp.Description,
  208. ResponseParameters: parameters,
  209. }
  210. }
  211. return &apiResp, nil
  212. }
  213. // GetFileDirectURL returns direct URL to file
  214. //
  215. // It requires the FileID.
  216. func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
  217. file, err := bot.GetFile(FileConfig{fileID})
  218. if err != nil {
  219. return "", err
  220. }
  221. return file.Link(bot.Token), nil
  222. }
  223. // GetMe fetches the currently authenticated bot.
  224. //
  225. // This method is called upon creation to validate the token,
  226. // and so you may get this data from BotAPI.Self without the need for
  227. // another request.
  228. func (bot *BotAPI) GetMe() (User, error) {
  229. resp, err := bot.MakeRequest("getMe", nil)
  230. if err != nil {
  231. return User{}, err
  232. }
  233. var user User
  234. err = json.Unmarshal(resp.Result, &user)
  235. return user, err
  236. }
  237. // IsMessageToMe returns true if message directed to this bot.
  238. //
  239. // It requires the Message.
  240. func (bot *BotAPI) IsMessageToMe(message Message) bool {
  241. return strings.Contains(message.Text, "@"+bot.Self.UserName)
  242. }
  243. func hasFilesNeedingUpload(files []RequestFile) bool {
  244. for _, file := range files {
  245. if file.Data.NeedsUpload() {
  246. return true
  247. }
  248. }
  249. return false
  250. }
  251. // Request sends a Chattable to Telegram, and returns the APIResponse.
  252. func (bot *BotAPI) Request(c Chattable) (*APIResponse, error) {
  253. params, err := c.params()
  254. if err != nil {
  255. return nil, err
  256. }
  257. if t, ok := c.(Fileable); ok {
  258. files := t.files()
  259. // If we have files that need to be uploaded, we should delegate the
  260. // request to UploadFile.
  261. if hasFilesNeedingUpload(files) {
  262. return bot.UploadFiles(t.method(), params, files)
  263. }
  264. // However, if there are no files to be uploaded, there's likely things
  265. // that need to be turned into params instead.
  266. for _, file := range files {
  267. params[file.Name] = file.Data.SendData()
  268. }
  269. }
  270. return bot.MakeRequest(c.method(), params)
  271. }
  272. // Send will send a Chattable item to Telegram and provides the
  273. // returned Message.
  274. func (bot *BotAPI) Send(c Chattable) (Message, error) {
  275. resp, err := bot.Request(c)
  276. if err != nil {
  277. return Message{}, err
  278. }
  279. var message Message
  280. err = json.Unmarshal(resp.Result, &message)
  281. return message, err
  282. }
  283. // SendMediaGroup sends a media group and returns the resulting messages.
  284. func (bot *BotAPI) SendMediaGroup(config MediaGroupConfig) ([]Message, error) {
  285. resp, err := bot.Request(config)
  286. if err != nil {
  287. return nil, err
  288. }
  289. var messages []Message
  290. err = json.Unmarshal(resp.Result, &messages)
  291. return messages, err
  292. }
  293. // GetUserProfilePhotos gets a user's profile photos.
  294. //
  295. // It requires UserID.
  296. // Offset and Limit are optional.
  297. func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
  298. resp, err := bot.Request(config)
  299. if err != nil {
  300. return UserProfilePhotos{}, err
  301. }
  302. var profilePhotos UserProfilePhotos
  303. err = json.Unmarshal(resp.Result, &profilePhotos)
  304. return profilePhotos, err
  305. }
  306. // GetFile returns a File which can download a file from Telegram.
  307. //
  308. // Requires FileID.
  309. func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
  310. resp, err := bot.Request(config)
  311. if err != nil {
  312. return File{}, err
  313. }
  314. var file File
  315. err = json.Unmarshal(resp.Result, &file)
  316. return file, err
  317. }
  318. // GetUpdates fetches updates.
  319. // If a WebHook is set, this will not return any data!
  320. //
  321. // Offset, Limit, Timeout, and AllowedUpdates are optional.
  322. // To avoid stale items, set Offset to one higher than the previous item.
  323. // Set Timeout to a large number to reduce requests, so you can get updates
  324. // instantly instead of having to wait between requests.
  325. func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
  326. resp, err := bot.Request(config)
  327. if err != nil {
  328. return []Update{}, err
  329. }
  330. var updates []Update
  331. err = json.Unmarshal(resp.Result, &updates)
  332. return updates, err
  333. }
  334. // GetWebhookInfo allows you to fetch information about a webhook and if
  335. // one currently is set, along with pending update count and error messages.
  336. func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
  337. resp, err := bot.MakeRequest("getWebhookInfo", nil)
  338. if err != nil {
  339. return WebhookInfo{}, err
  340. }
  341. var info WebhookInfo
  342. err = json.Unmarshal(resp.Result, &info)
  343. return info, err
  344. }
  345. // GetUpdatesChan starts and returns a channel for getting updates.
  346. func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel {
  347. ch := make(chan Update, bot.Buffer)
  348. go func() {
  349. for {
  350. select {
  351. case <-bot.shutdownChannel:
  352. close(ch)
  353. return
  354. default:
  355. }
  356. updates, err := bot.GetUpdates(config)
  357. if err != nil {
  358. log.Println(err)
  359. log.Println("Failed to get updates, retrying in 3 seconds...")
  360. time.Sleep(time.Second * 3)
  361. continue
  362. }
  363. for _, update := range updates {
  364. if update.UpdateID >= config.Offset {
  365. config.Offset = update.UpdateID + 1
  366. ch <- update
  367. }
  368. }
  369. }
  370. }()
  371. return ch
  372. }
  373. // StopReceivingUpdates stops the go routine which receives updates
  374. func (bot *BotAPI) StopReceivingUpdates() {
  375. if bot.Debug {
  376. log.Println("Stopping the update receiver routine...")
  377. }
  378. close(bot.shutdownChannel)
  379. }
  380. // ListenForWebhook registers a http handler for a webhook.
  381. func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
  382. ch := make(chan Update, bot.Buffer)
  383. http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
  384. update, err := bot.HandleUpdate(r)
  385. if err != nil {
  386. errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
  387. w.WriteHeader(http.StatusBadRequest)
  388. w.Header().Set("Content-Type", "application/json")
  389. _, _ = w.Write(errMsg)
  390. return
  391. }
  392. ch <- *update
  393. })
  394. return ch
  395. }
  396. // ListenForWebhookRespReqFormat registers a http handler for a single incoming webhook.
  397. func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.Request) UpdatesChannel {
  398. ch := make(chan Update, bot.Buffer)
  399. func(w http.ResponseWriter, r *http.Request) {
  400. defer close(ch)
  401. update, err := bot.HandleUpdate(r)
  402. if err != nil {
  403. errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
  404. w.WriteHeader(http.StatusBadRequest)
  405. w.Header().Set("Content-Type", "application/json")
  406. _, _ = w.Write(errMsg)
  407. return
  408. }
  409. ch <- *update
  410. }(w, r)
  411. return ch
  412. }
  413. // HandleUpdate parses and returns update received via webhook
  414. func (bot *BotAPI) HandleUpdate(r *http.Request) (*Update, error) {
  415. if r.Method != http.MethodPost {
  416. err := errors.New("wrong HTTP method required POST")
  417. return nil, err
  418. }
  419. var update Update
  420. err := json.NewDecoder(r.Body).Decode(&update)
  421. if err != nil {
  422. return nil, err
  423. }
  424. return &update, nil
  425. }
  426. // WriteToHTTPResponse writes the request to the HTTP ResponseWriter.
  427. //
  428. // It doesn't support uploading files.
  429. //
  430. // See https://core.telegram.org/bots/api#making-requests-when-getting-updates
  431. // for details.
  432. func WriteToHTTPResponse(w http.ResponseWriter, c Chattable) error {
  433. params, err := c.params()
  434. if err != nil {
  435. return err
  436. }
  437. if t, ok := c.(Fileable); ok {
  438. if hasFilesNeedingUpload(t.files()) {
  439. return errors.New("unable to use http response to upload files")
  440. }
  441. }
  442. values := buildParams(params)
  443. values.Set("method", c.method())
  444. w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
  445. _, err = w.Write([]byte(values.Encode()))
  446. return err
  447. }
  448. // GetChat gets information about a chat.
  449. func (bot *BotAPI) GetChat(config ChatInfoConfig) (Chat, error) {
  450. resp, err := bot.Request(config)
  451. if err != nil {
  452. return Chat{}, err
  453. }
  454. var chat Chat
  455. err = json.Unmarshal(resp.Result, &chat)
  456. return chat, err
  457. }
  458. // GetChatAdministrators gets a list of administrators in the chat.
  459. //
  460. // If none have been appointed, only the creator will be returned.
  461. // Bots are not shown, even if they are an administrator.
  462. func (bot *BotAPI) GetChatAdministrators(config ChatAdministratorsConfig) ([]ChatMember, error) {
  463. resp, err := bot.Request(config)
  464. if err != nil {
  465. return []ChatMember{}, err
  466. }
  467. var members []ChatMember
  468. err = json.Unmarshal(resp.Result, &members)
  469. return members, err
  470. }
  471. // GetChatMembersCount gets the number of users in a chat.
  472. func (bot *BotAPI) GetChatMembersCount(config ChatMemberCountConfig) (int, error) {
  473. resp, err := bot.Request(config)
  474. if err != nil {
  475. return -1, err
  476. }
  477. var count int
  478. err = json.Unmarshal(resp.Result, &count)
  479. return count, err
  480. }
  481. // GetChatMember gets a specific chat member.
  482. func (bot *BotAPI) GetChatMember(config GetChatMemberConfig) (ChatMember, error) {
  483. resp, err := bot.Request(config)
  484. if err != nil {
  485. return ChatMember{}, err
  486. }
  487. var member ChatMember
  488. err = json.Unmarshal(resp.Result, &member)
  489. return member, err
  490. }
  491. // GetGameHighScores allows you to get the high scores for a game.
  492. func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
  493. resp, err := bot.Request(config)
  494. if err != nil {
  495. return []GameHighScore{}, err
  496. }
  497. var highScores []GameHighScore
  498. err = json.Unmarshal(resp.Result, &highScores)
  499. return highScores, err
  500. }
  501. // GetInviteLink get InviteLink for a chat
  502. func (bot *BotAPI) GetInviteLink(config ChatInviteLinkConfig) (string, error) {
  503. resp, err := bot.Request(config)
  504. if err != nil {
  505. return "", err
  506. }
  507. var inviteLink string
  508. err = json.Unmarshal(resp.Result, &inviteLink)
  509. return inviteLink, err
  510. }
  511. // GetStickerSet returns a StickerSet.
  512. func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
  513. resp, err := bot.Request(config)
  514. if err != nil {
  515. return StickerSet{}, err
  516. }
  517. var stickers StickerSet
  518. err = json.Unmarshal(resp.Result, &stickers)
  519. return stickers, err
  520. }
  521. // StopPoll stops a poll and returns the result.
  522. func (bot *BotAPI) StopPoll(config StopPollConfig) (Poll, error) {
  523. resp, err := bot.Request(config)
  524. if err != nil {
  525. return Poll{}, err
  526. }
  527. var poll Poll
  528. err = json.Unmarshal(resp.Result, &poll)
  529. return poll, err
  530. }
  531. // GetMyCommands gets the currently registered commands.
  532. func (bot *BotAPI) GetMyCommands() ([]BotCommand, error) {
  533. return bot.GetMyCommandsWithConfig(GetMyCommandsConfig{})
  534. }
  535. // GetMyCommandsWithConfig gets the currently registered commands with a config.
  536. func (bot *BotAPI) GetMyCommandsWithConfig(config GetMyCommandsConfig) ([]BotCommand, error) {
  537. resp, err := bot.Request(config)
  538. if err != nil {
  539. return nil, err
  540. }
  541. var commands []BotCommand
  542. err = json.Unmarshal(resp.Result, &commands)
  543. return commands, err
  544. }
  545. // CopyMessage copy messages of any kind. The method is analogous to the method
  546. // forwardMessage, but the copied message doesn't have a link to the original
  547. // message. Returns the MessageID of the sent message on success.
  548. func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) {
  549. resp, err := bot.Request(config)
  550. if err != nil {
  551. return MessageID{}, err
  552. }
  553. var messageID MessageID
  554. err = json.Unmarshal(resp.Result, &messageID)
  555. return messageID, err
  556. }
  557. // AnswerWebAppQuery sets the result of an interaction with a Web App and send a
  558. // corresponding message on behalf of the user to the chat from which the query originated.
  559. func (bot *BotAPI) AnswerWebAppQuery(config AnswerWebAppQueryConfig) (SentWebAppMessage, error) {
  560. var sentWebAppMessage SentWebAppMessage
  561. resp, err := bot.Request(config)
  562. if err != nil {
  563. return sentWebAppMessage, err
  564. }
  565. err = json.Unmarshal(resp.Result, &sentWebAppMessage)
  566. return sentWebAppMessage, err
  567. }
  568. // GetMyDefaultAdministratorRights gets the current default administrator rights of the bot.
  569. func (bot *BotAPI) GetMyDefaultAdministratorRights(config GetMyDefaultAdministratorRightsConfig) (ChatAdministratorRights, error) {
  570. var rights ChatAdministratorRights
  571. resp, err := bot.Request(config)
  572. if err != nil {
  573. return rights, err
  574. }
  575. err = json.Unmarshal(resp.Result, &rights)
  576. return rights, err
  577. }
  578. // EscapeText takes an input text and escape Telegram markup symbols.
  579. // In this way we can send a text without being afraid of having to escape the characters manually.
  580. // Note that you don't have to include the formatting style in the input text, or it will be escaped too.
  581. // If there is an error, an empty string will be returned.
  582. //
  583. // parseMode is the text formatting mode (ModeMarkdown, ModeMarkdownV2 or ModeHTML)
  584. // text is the input string that will be escaped
  585. func EscapeText(parseMode string, text string) string {
  586. var replacer *strings.Replacer
  587. if parseMode == ModeHTML {
  588. replacer = strings.NewReplacer("<", "&lt;", ">", "&gt;", "&", "&amp;")
  589. } else if parseMode == ModeMarkdown {
  590. replacer = strings.NewReplacer("_", "\\_", "*", "\\*", "`", "\\`", "[", "\\[")
  591. } else if parseMode == ModeMarkdownV2 {
  592. replacer = strings.NewReplacer(
  593. "_", "\\_", "*", "\\*", "[", "\\[", "]", "\\]", "(",
  594. "\\(", ")", "\\)", "~", "\\~", "`", "\\`", ">", "\\>",
  595. "#", "\\#", "+", "\\+", "-", "\\-", "=", "\\=", "|",
  596. "\\|", "{", "\\{", "}", "\\}", ".", "\\.", "!", "\\!",
  597. )
  598. } else {
  599. return ""
  600. }
  601. return replacer.Replace(text)
  602. }