diff --git a/bot.go b/bot.go index 30030e9..c2660d0 100644 --- a/bot.go +++ b/bot.go @@ -7,7 +7,6 @@ import ( "fmt" "log" "math" - "math/rand" "net/http" "os" "regexp" @@ -16,6 +15,8 @@ import ( "github.com/gotd/td/session" "github.com/gotd/td/telegram" "github.com/gotd/td/telegram/auth" + "github.com/gotd/td/telegram/message" + "github.com/gotd/td/telegram/message/html" "github.com/gotd/td/telegram/updates" "github.com/gotd/td/tg" "golang.org/x/term" @@ -33,6 +34,7 @@ type Config struct { APIHash string `yaml:"api_hash"` MonitoredChat int64 `yaml:"monitored_chat"` AlertChat int64 `yaml:"alert_chat"` + NtfyHost string `json:"ntfy_host"` NtfyToken string `yaml:"ntfy_token"` NtfyTopic string `yaml:"ntfy_topic"` SessionPath string `yaml:"session_path"` @@ -49,6 +51,7 @@ func loadConfig(path string) (*Config, error) { api_hash: "" monitored_chat: 0 alert_chat: 0 +ntfy_host: "https://ntfy.sh/" ntfy_token: "" ntfy_topic: "" session_path: "antiscam.session" @@ -192,8 +195,32 @@ func escapeMarkdown(text string) string { return result.String() } -func notify(message, topic, title string, priority int, token string) { - url := "https://ntfy.zio.sh/" + topic +func escapeHTML(text string) string { + var result strings.Builder + for _, r := range text { + switch r { + case '&': + result.WriteString("&") + case '<': + result.WriteString("<") + case '>': + result.WriteString(">") + case '"': + result.WriteString(""") + case '\'': + result.WriteString("'") + default: + result.WriteRune(r) + } + } + return result.String() +} + +func notify(message, host, topic, title string, priority int, token string) { + if !strings.HasSuffix(host, "/") { + host = host + "/" + } + url := host + topic req, err := http.NewRequest("POST", url, bytes.NewBufferString(message)) if err != nil { log.Printf("notify: creating request: %v", err) @@ -390,21 +417,29 @@ func handleNewMessage(ctx context.Context, api *tg.Client, alertPeer *tg.InputPe // Get supergroup name chatName := channel.Title - // Build and send alert message - matchMessage := fmt.Sprintf("🚨 Matched\n**Score**: %.2f\n**Chat**: %s (ID: %d)\n**User**: %s (%s) (ID: %d)\n", - result.score, escapeMarkdown(chatName), chatID, escapeMarkdown(displayName+" ("+username+")"), username, senderID) + // Build alert message with HTML formatting for markdown v2 + matchMessageHTML := fmt.Sprintf("🚨 Matched\nScore: %.2f\nChat: %s (ID: %d)\nUser: %s (%s) (ID: %d)\n", + result.score, escapeHTML(chatName), chatID, escapeHTML(displayName+" ("+username+")"), username, senderID) - // Send ntfy notification if config set + // Send ntfy notification if config set (use plain text for ntfy) if cfg.NtfyToken != "" || cfg.NtfyTopic != "" { - notify(matchMessage, cfg.NtfyTopic, fmt.Sprintf("Scam Alert: %s", chatName), 5, cfg.NtfyToken) + plainMessage := fmt.Sprintf("🚨 Matched\nScore: %.2f\nChat: %s (ID: %d)\nUser: %s (%s) (ID: %d)\n", + result.score, chatName, chatID, displayName+" ("+username+")", username, senderID) + notify(plainMessage, cfg.NtfyHost, cfg.NtfyTopic, fmt.Sprintf("Scam Alert: %s", chatName), 5, cfg.NtfyToken) } - // Send alert message to alert chat - _, err = api.MessagesSendMessage(ctx, &tg.MessagesSendMessageRequest{ - Peer: alertPeer, - Message: matchMessage, - RandomID: rand.Int63(), - }) + // Create a resolver for user mentions (not needed for this message, but required by html.String) + userResolver := func(id int64) (tg.InputUserClass, error) { + return &tg.InputUserFromMessage{ + Peer: alertPeer, + MsgID: 0, + UserID: id, + }, nil + } + + // Send alert message to alert chat using StyledText with HTML + sender := message.NewSender(api) + _, err = sender.To(alertPeer).StyledText(ctx, html.String(userResolver, matchMessageHTML)) if err != nil { log.Printf("Failed to send alert message: %v", err) }