feat: telegram notifier

This commit is contained in:
chethober
2026-01-30 01:30:35 +03:30
parent 58c8b4896b
commit 6af6b3d890
8 changed files with 392 additions and 15 deletions

View File

@@ -19,7 +19,7 @@ type Config struct {
TestTimeout time.Duration `envconfig:"TEST_TIMEOUT" default:"10s"`
// File System Paths
SingBoxPath string `envconfig:"SING_BOX_PATH" default:"./bin/sing-box"`
SingBoxPath string `envconfig:"SING_BOX_PATH" default:"/usr/bin/sing-box"`
InputPath string `envconfig:"INPUT_PATH" default:"proxies.txt"`
OutputPath string `envconfig:"OUTPUT_PATH" default:"valid.jsonl"`
GeoIPPath string `envconfig:"GEOIP_PATH" default:"GeoLite2-Country.mmdb"`
@@ -27,6 +27,10 @@ type Config struct {
AliveOutputPath string `envconfig:"ALIVE_OUTPUT_PATH" default:"alive.jsonl"`
AliveTxtOutputPath string `envconfig:"ALIVE_TXT_OUTPUT_PATH" default:"alive.txt"`
DatasetOutputPath string `envconfig:"DATASET_OUTPUT_PATH" default:"dataset.jsonl"`
// Telegram Bot Settings
TelegramBotToken string `envconfig:"TELEGRAM_BOT_TOKEN"`
TelegramChatID string `envconfig:"TELEGRAM_CHAT_ID"`
}
// Load reads .env and processes environment variables

View File

@@ -0,0 +1,91 @@
package telegram
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"golang.org/x/time/rate"
)
type Notifier struct {
botToken string
chatID string
client *http.Client
limiter *rate.Limiter
}
func NewNotifier(botToken, chatID string) *Notifier {
return &Notifier{
botToken: botToken,
chatID: chatID,
client: &http.Client{Timeout: 10 * time.Second},
limiter: rate.NewLimiter(rate.Every(time.Second/30), 1), // 30 messages per second
}
}
// SendMessage sends a text message to the configured chat
func (n *Notifier) SendMessage(text string) error {
// Wait for rate limiter
if err := n.limiter.Wait(context.Background()); err != nil {
return fmt.Errorf("rate limiter error: %w", err)
}
// Prepare the request URL
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", n.botToken)
// Create the request body
reqBody := map[string]string{
"chat_id": n.chatID,
"text": text,
}
jsonBody, err := json.Marshal(reqBody)
if err != nil {
return fmt.Errorf("error marshaling request: %w", err)
}
// Create and send the request
resp, err := n.client.Post(url, "application/json", bytes.NewBuffer(jsonBody))
if err != nil {
return fmt.Errorf("error sending request: %w", err)
}
defer resp.Body.Close()
// Check response status
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("telegram API error: %s - %s", resp.Status, string(body))
}
return nil
}
// SendProxiesFromFile reads proxies from a file and sends them to the chat
func (n *Notifier) SendProxiesFromFile(filePath string) error {
// Read the file
content, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("error reading file: %w", err)
}
// Split by lines and send each line as a message
lines := strings.Split(string(content), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if err := n.SendMessage(line); err != nil {
return fmt.Errorf("error sending message: %w", err)
}
}
return nil
}