From f5a793a907dbbb8a3a57a64de88d48b697138c31 Mon Sep 17 00:00:00 2001 From: Jemy Zhang Date: Sat, 21 Mar 2020 13:14:46 +0800 Subject: [PATCH] Add support to telegram notification --- README.md | 30 ++++++-- config_sample.json | 20 +++-- handler/alidns/alidns_handler.go | 9 +-- handler/cloudflare/cloudflare_handler.go | 9 +-- handler/dnspod/dnspod_handler.go | 10 +-- handler/duck/duck_handler.go | 9 +-- handler/google/google_handler.go | 9 +-- handler/he/he_handler.go | 9 +-- settings.go | 16 +++- utils.go | 96 ++++++++++++++++++++++-- 10 files changed, 159 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 55f0568..5d82e2f 100644 --- a/README.md +++ b/README.md @@ -337,12 +337,14 @@ Update config file and provide your SMTP options, a notification mail will be se ```json "notify": { - "enabled": true, - "smtp_server": "smtp.example.com", - "smtp_username": "user", - "smtp_password": "password", - "smtp_port": 25, - "send_to": "my_mail@example.com" + "mail": { + "enabled": true, + "smtp_server": "smtp.example.com", + "smtp_username": "user", + "smtp_password": "password", + "smtp_port": 25, + "send_to": "my_mail@example.com" + } } ``` @@ -350,6 +352,22 @@ Notification mail example: +### Telegram notification support + +Update config file and provide your Telegram options, a notification message will be sent to your telegram channel once the IP is changed and updated. + +```json + "notify": { + "telegram": { + "enabled": true, + "bot_api_key": "11111:aaaa-bbbb", + "chat_id": "-123456", + "message_template": "Domain *{{ .Domain }}* is updated to %0A{{ .CurrentIP }}" + }, + } +``` +Markdown is supported in message template, and use `%0A` for newline. + ### SOCKS5 proxy support You can also use SOCKS5 proxy, just fill SOCKS5 address to the ```socks5_proxy``` item: diff --git a/config_sample.json b/config_sample.json index 86a2785..c5fccac 100644 --- a/config_sample.json +++ b/config_sample.json @@ -26,11 +26,19 @@ "ip_interface": "eth0", "socks5_proxy": "", "notify": { - "enabled": false, - "smtp_server": "", - "smtp_username": "", - "smtp_password": "", - "smtp_port": 25, - "send_to": "" + "telegram": { + "enabled": false, + "bot_api_key": "", + "chat_id": "", + "message_template": "" + }, + "mail": { + "enabled": false, + "smtp_server": "", + "smtp_username": "", + "smtp_password": "", + "smtp_port": 25, + "send_to": "" + } } } diff --git a/handler/alidns/alidns_handler.go b/handler/alidns/alidns_handler.go index 94c63af..a5f92c5 100644 --- a/handler/alidns/alidns_handler.go +++ b/handler/alidns/alidns_handler.go @@ -62,12 +62,9 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns. log.Printf("IP updated for subdomain:%s\r\n", subDomain) } - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { - log.Printf("Failed to send notification") - } + // Send notification + if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { + log.Printf("Failed to send notification") } } } diff --git a/handler/cloudflare/cloudflare_handler.go b/handler/cloudflare/cloudflare_handler.go index 64f96b8..4c9c24c 100644 --- a/handler/cloudflare/cloudflare_handler.go +++ b/handler/cloudflare/cloudflare_handler.go @@ -105,12 +105,9 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns. log.Printf("IP mismatch: Current(%+v) vs Cloudflare(%+v)\r\n", currentIP, rec.IP) handler.updateRecord(rec, currentIP) - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - if err := godns.SendNotify(handler.Configuration, rec.Name, currentIP); err != nil { - log.Println("Failed to send notification") - } + // Send notification + if err := godns.SendNotify(handler.Configuration, rec.Name, currentIP); err != nil { + log.Println("Failed to send notification") } } else { log.Printf("Record OK: %+v - %+v\r\n", rec.Name, rec.IP) diff --git a/handler/dnspod/dnspod_handler.go b/handler/dnspod/dnspod_handler.go index 678945a..1df4d74 100644 --- a/handler/dnspod/dnspod_handler.go +++ b/handler/dnspod/dnspod_handler.go @@ -73,14 +73,10 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns. log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) handler.UpdateIP(domainID, subDomainID, subDomain, currentIP) - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { - log.Println("Failed to send notification") - } + // Send notification + if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { + log.Println("Failed to send notification") } - } else { log.Printf("%s.%s Current IP is same as domain IP, no need to update...\n", subDomain, domain.DomainName) } diff --git a/handler/duck/duck_handler.go b/handler/duck/duck_handler.go index 4e54e25..6be7bc8 100644 --- a/handler/duck/duck_handler.go +++ b/handler/duck/duck_handler.go @@ -79,12 +79,9 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns. log.Print("IP updated to:", currentIP) } - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { - log.Println("Failed to send notification") - } + // Send notification + if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { + log.Println("Failed to send notification") } } } diff --git a/handler/google/google_handler.go b/handler/google/google_handler.go index c1fefb8..083613f 100644 --- a/handler/google/google_handler.go +++ b/handler/google/google_handler.go @@ -58,12 +58,9 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns. log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) handler.UpdateIP(domain.DomainName, subDomain, currentIP) - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { - log.Println("Failed to send notification") - } + // Send notification + if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { + log.Println("Failed to send notification") } } } diff --git a/handler/he/he_handler.go b/handler/he/he_handler.go index 9c130bc..7c05f66 100644 --- a/handler/he/he_handler.go +++ b/handler/he/he_handler.go @@ -57,12 +57,9 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns. log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) handler.UpdateIP(domain.DomainName, subDomain, currentIP) - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { - log.Println("Failed to send notification") - } + // Send notification + if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil { + log.Println("Failed to send notification") } } } diff --git a/settings.go b/settings.go index 0609450..fbf945f 100644 --- a/settings.go +++ b/settings.go @@ -12,8 +12,16 @@ type Domain struct { SubDomains []string `json:"sub_domains"` } +// Notify struct for telegram notification +type TelegramNotify struct { + Enabled bool `json:"enabled"` + BotApiKey string `json:"bot_api_key"` + ChatId string `json:"chat_id"` + MsgTemplate string `json:"message_template"` +} + // Notify struct for SMTP notification -type Notify struct { +type MailNotify struct { Enabled bool `json:"enabled"` SMTPServer string `json:"smtp_server"` SMTPUsername string `json:"smtp_username"` @@ -22,6 +30,12 @@ type Notify struct { SendTo string `json:"send_to"` } +// Notify struct +type Notify struct { + Telegram TelegramNotify `json:"telegram"` + Mail MailNotify `json:"mail"` +} + // Settings struct type Settings struct { Provider string `json:"provider"` diff --git a/utils.go b/utils.go index 8ec6974..1b26805 100644 --- a/utils.go +++ b/utils.go @@ -2,7 +2,9 @@ package godns import ( "bytes" + "encoding/json" "errors" + "fmt" "html/template" "io/ioutil" "log" @@ -234,30 +236,108 @@ func CheckSettings(config *Settings) error { return nil } +// SendNotify sends notify if IP is changed +func SendTelegramNotify(configuration *Settings, domain, currentIP string) error { + if ! configuration.Notify.Telegram.Enabled { + return nil + } + + if configuration.Notify.Telegram.BotApiKey == "" { + return errors.New("bot api key cannot be empty") + } + + if configuration.Notify.Telegram.ChatId == "" { + return errors.New("chat id cannot be empty") + } + + + client := GetHttpClient(configuration) + tpl := configuration.Notify.Telegram.MsgTemplate + if tpl == "" { + tpl = "_Your IP address is changed to_%0A%0A*{{ .CurrentIP }}*%0A%0ADomain *{{ .Domain }}* is updated" + } + + msg := buildTemplate(currentIP, domain, tpl) + url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&parse_mode=Markdown&text=%s", + configuration.Notify.Telegram.BotApiKey, + configuration.Notify.Telegram.ChatId, + msg) + var response *http.Response + var err error + + response, err = client.Get(url) + + if err != nil { + return err + } + + defer response.Body.Close() + + body, _ := ioutil.ReadAll(response.Body) + type ResponseParameters struct { + MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional + RetryAfter int `json:"retry_after"` // optional + } + type APIResponse struct { + Ok bool `json:"ok"` + Result json.RawMessage `json:"result"` + ErrorCode int `json:"error_code"` + Description string `json:"description"` + Parameters *ResponseParameters `json:"parameters"` + } + var resp APIResponse + err = json.Unmarshal([]byte(body), &resp) + if err != nil { + fmt.Println("error:", err) + return errors.New("Failed to parse response") + } + if ! resp.Ok { + return errors.New(resp.Description) + } + + return nil +} + // SendNotify sends mail notify if IP is changed -func SendNotify(configuration *Settings, domain, currentIP string) error { +func SendMailNotify(configuration *Settings, domain, currentIP string) error { + if ! configuration.Notify.Mail.Enabled { + return nil + } + log.Print("Sending notification to:", configuration.Notify.Mail.SendTo) m := gomail.NewMessage() - m.SetHeader("From", configuration.Notify.SMTPUsername) - m.SetHeader("To", configuration.Notify.SendTo) + m.SetHeader("From", configuration.Notify.Mail.SMTPUsername) + m.SetHeader("To", configuration.Notify.Mail.SendTo) m.SetHeader("Subject", "GoDNS Notification") log.Println("currentIP:", currentIP) log.Println("domain:", domain) - m.SetBody("text/html", buildTemplate(currentIP, domain)) + m.SetBody("text/html", buildTemplate(currentIP, domain, mailTemplate)) - d := gomail.NewPlainDialer(configuration.Notify.SMTPServer, configuration.Notify.SMTPPort, configuration.Notify.SMTPUsername, configuration.Notify.SMTPPassword) + d := gomail.NewPlainDialer(configuration.Notify.Mail.SMTPServer, configuration.Notify.Mail.SMTPPort, configuration.Notify.Mail.SMTPUsername, configuration.Notify.Mail.SMTPPassword) // Send the email config by sendlist . if err := d.DialAndSend(m); err != nil { - log.Println("Send email notification with error:", err.Error()) return err } return nil } -func buildTemplate(currentIP, domain string) string { +// SendNotify sends notify if IP is changed +func SendNotify(configuration *Settings, domain, currentIP string) error { + err := SendTelegramNotify(configuration, domain, currentIP) + if (err != nil) { + log.Println("Send telegram notification with error:", err.Error()) + } + err = SendMailNotify(configuration, domain, currentIP) + if (err != nil) { + log.Println("Send email notification with error:", err.Error()) + } + return nil +} + +func buildTemplate(currentIP, domain string, tplsrc string) string { t := template.New("notification template") - if _, err := t.Parse(mailTemplate); err != nil { + if _, err := t.Parse(tplsrc); err != nil { log.Println("Failed to parse template") return "" }