telegram/replace.go (view raw)
1package telegram
2
3import (
4 "fmt"
5 "regexp"
6 "strings"
7
8 tgbotapi "github.com/BiRabittoh/telegram-bot-api/v5"
9)
10
11type Replacer struct {
12 Regex *regexp.Regexp
13 Format string
14}
15
16const (
17 parseMode = "markdown"
18 linkMessage = "[🔗](%s) Da %s."
19 regexFlags = "(?i)(?m)"
20)
21
22var replacers = []Replacer{
23 {
24 Regex: regexp.MustCompile(regexFlags + `(?:(?:https?:)?\/\/)?(?:(?:www|m)\.)?(?:(?:youtube(?:-nocookie)?\.com|youtu.be))(?:\/(?:[\w\-]+\?v=|embed\/|live\/|v\/|shorts\/)?)([\w\-]+)`),
25 Format: "https://y.outube.duckdns.org/%s",
26 },
27 {
28 Regex: regexp.MustCompile(regexFlags + `https?:\/\/(?:www\.)?twitter\.com\/(?:#!\/)?(.*)\/status(?:es)?\/([^\/\?\s]+)`),
29 Format: "https://fxtwitter.com/%s/status/%s",
30 },
31 {
32 Regex: regexp.MustCompile(regexFlags + `(?:https?:\/\/)?(?:www\.)?x\.com\/(?:#!\/)?(.*)\/status(?:es)?\/([^\/\?\s]+)`),
33 Format: "https://fixupx.com/%s/status/%s",
34 },
35 {
36 Regex: regexp.MustCompile(regexFlags + `(?:https?:\/\/)?(?:www\.)?instagram\.com\/(reels?|p)\/([\w\-]{11})[\/\?\w=&]*`),
37 Format: "https://ddinstagram.com/%s/%s",
38 },
39 {
40 Regex: regexp.MustCompile(regexFlags + `https?:\/\/(?:(?:www)|(?:vm))?\.?tiktok\.com\/@([\w.]+)\/(?:video)\/(\d{19,})`),
41 Format: "https://www.tnktok.com/@%s/video/%s",
42 },
43 {
44 Regex: regexp.MustCompile(regexFlags + `(?:https?:\/\/)?(?:(?:www)|(?:vm))?\.?tiktok\.com\/(?:t\/)?([\w]{9})\/?`),
45 Format: "https://vm.tnktok.com/%s/",
46 },
47}
48
49func isInSpoiler(entities []tgbotapi.MessageEntity, offset, length int) bool {
50 for _, s := range entities {
51 if s.Type != "spoiler" {
52 continue
53 }
54
55 if offset < s.Offset+s.Length && offset+length > s.Offset {
56 return true
57 }
58 }
59 return false
60}
61
62func parseText(text string, entities []tgbotapi.MessageEntity) (links []string) {
63 var rawLinks string
64 runes := []rune(text) // Convert to runes to handle emojis
65
66 for _, e := range entities {
67 if e.Type == "text_link" {
68 if isInSpoiler(entities, e.Offset, len(e.URL)) {
69 continue
70 }
71 rawLinks += e.URL + "\n"
72 } else if e.Type == "url" {
73 if isInSpoiler(entities, e.Offset, e.Length) {
74 continue
75 }
76
77 rawLinks += string(runes[e.Offset:e.Offset+e.Length]) + "\n"
78 }
79 }
80
81 for _, replacer := range replacers {
82 foundMatches := replacer.Regex.FindStringSubmatch(rawLinks)
83 if len(foundMatches) == 0 {
84 continue
85 }
86 captureGroups := foundMatches[1:]
87
88 var formatArgs []interface{}
89 for _, match := range captureGroups {
90 if match != "" {
91 formatArgs = append(formatArgs, match)
92 }
93 }
94
95 formatted := fmt.Sprintf(replacer.Format, formatArgs...)
96 links = append(links, formatted)
97 }
98 return links
99}
100
101func getUserMention(user tgbotapi.User) string {
102 var name string
103 if user.UserName == "" {
104 name = strings.TrimSpace(user.FirstName + " " + user.LastName)
105 } else {
106 name = "@" + user.UserName
107 }
108 return fmt.Sprintf("[%s](tg://user?id=%d)", name, user.ID)
109}
110
111func handleLinks(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
112 links := []string{}
113
114 if len(message.Entities) > 0 {
115 textLinks := parseText(message.Text, message.Entities)
116 links = append(links, textLinks...)
117 }
118
119 if len(message.CaptionEntities) > 0 {
120 captionLinks := parseText(message.Caption, message.CaptionEntities)
121 links = append(links, captionLinks...)
122 }
123
124 if len(links) == 0 {
125 return
126 }
127
128 user := getUserMention(*message.From)
129
130 for _, link := range links {
131 text := fmt.Sprintf(linkMessage, link, user)
132 msg := tgbotapi.NewMessage(message.Chat.ID, text)
133 msg.MessageThreadID = message.MessageThreadID
134 msg.ParseMode = parseMode
135 bot.Send(msg)
136 }
137
138}