diff --git a/.env.example b/.env.example index 2a1e8fb..25777c7 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ DISCORD_WEBHOOKS= // Comma separated SLACK_WEBHOOKS= // webhook1!channel1,webhook2!channel2 Comma separated with ! for channel hash (Example https://hooks.slack.com/abc/def!covid-19) + +TELEGRAM_BOT_TOKENS= // bot123:456!-12345 - Seperate channel ID with a ! (-12345 here) \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dca47c5..5ba10c9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,7 @@ stages: - build variables: - VERSION: 0.1.3 + VERSION: 0.1.4 build-go: image: golang:1.17 diff --git a/CHANGELOG.md b/CHANGELOG.md index d3de0c2..7e9b2ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +# 1.4 +- Add telegram postings (currently t.me/nzcovidlocations) + # 1.3 - Omicron related ones will post with an asterisk to indicate - Group different spelling locations together diff --git a/cmd/nzcovidbot/main.go b/cmd/nzcovidbot/main.go index 7ae85e6..185aa3e 100644 --- a/cmd/nzcovidbot/main.go +++ b/cmd/nzcovidbot/main.go @@ -24,6 +24,9 @@ func main() { // Fetch slack webhooks nzcovidbot.SlackWebhooks = strings.Split(os.Getenv("SLACK_WEBHOOKS"), ",") + // Fetch slack webhooks + nzcovidbot.TelegramTokens = strings.Split(os.Getenv("TELEGRAM_BOT_TOKENS"), ",") + // Boot up listeners / main loop nzcovidbot.Lesgoooo() } diff --git a/go.mod b/go.mod index eac6fe3..13a269c 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960 github.com/davecgh/go-spew v1.1.1 // indirect github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 // indirect + github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect github.com/joho/godotenv v1.3.0 github.com/parnurzeal/gorequest v0.2.16 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/go.sum b/go.sum index 10bb957..2278549 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY= github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= +github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= diff --git a/internal/nzcovidbot/api.go b/internal/nzcovidbot/api.go index f6e2bca..772e536 100644 --- a/internal/nzcovidbot/api.go +++ b/internal/nzcovidbot/api.go @@ -57,7 +57,7 @@ func fetchAPILocations() (ApiResponse, error) { } // Set user-agent info - req.Header.Set("User-Agent", "NZCovidBot/1.3 (https://m2.nz)") + req.Header.Set("User-Agent", "NZCovidBot/1.4 (https://m2.nz)") // Fire off the request resp, err := client.Do(req) @@ -123,7 +123,6 @@ func getNewAPILocations() { } } } - } // Make sure to clear out the previous list and append new data in a map based on location diff --git a/internal/nzcovidbot/discord.go b/internal/nzcovidbot/discord.go index 5f50b40..d16e5d7 100644 --- a/internal/nzcovidbot/discord.go +++ b/internal/nzcovidbot/discord.go @@ -87,9 +87,8 @@ func getPostableDiscordData() map[string][]string { return groups } -// formatCsvDiscordRow Format the string to a tidy string for the interwebs +// getDiscordRow Format the string to a tidy string for the interwebs func getDiscordRow(item ApiItem) string { return fmt.Sprintf("**%s** %s on _%s_", item.EventName, item.Location.Address, item.getDateString()) - } diff --git a/internal/nzcovidbot/nzcovidbot.go b/internal/nzcovidbot/nzcovidbot.go index cfb98aa..2b4c442 100644 --- a/internal/nzcovidbot/nzcovidbot.go +++ b/internal/nzcovidbot/nzcovidbot.go @@ -66,6 +66,9 @@ func getLastPollTime() *time.Time { } func postTheUpdates() { + // Telegram + go postToTelegram() + // Slack go postToSlack() diff --git a/internal/nzcovidbot/telegram.go b/internal/nzcovidbot/telegram.go new file mode 100644 index 0000000..8612ee5 --- /dev/null +++ b/internal/nzcovidbot/telegram.go @@ -0,0 +1,122 @@ +package nzcovidbot + +import ( + "fmt" + "log" + "strconv" + "strings" + "time" + + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" +) + +// Slice of discord webhooks +var TelegramTokens []string + +// Max locations per telegram post +var TelegramMaxPerPost = 100 + +func postToTelegram() { + postableTelegramData := getPostableTelegramData() + if len(postableTelegramData) == 0 { + return + } + + for _, telegramToken := range TelegramTokens { + if telegramToken != "" { + tokenParts := strings.Split(telegramToken, "!") + len := len(tokenParts) + if len != 2 { + log.Println("Telegram token error, channel ID or token invalid") + continue + } + + bot, err := tgbotapi.NewBotAPI(tokenParts[len-2]) + if err != nil { + log.Print(err) + continue + } + + // Build message and send for each location + for location, postableData := range postableTelegramData { + for _, post := range postableData { + chanId, err := strconv.ParseInt(tokenParts[len-1], 10, 64) + if err != nil { + continue + } + + // Create msg + msg := tgbotapi.NewMessage(chanId, ""+telegramEscape(location)+"\n\n"+post) + + // Decided to use HTML here as Markdown has way too many restricted chars :< + msg.ParseMode = tgbotapi.ModeHTML + + // SEND IT + _, err = bot.Send(msg) + if err != nil { + log.Print(err) + } + + time.Sleep(500 * time.Millisecond) + } + } + } + } +} + +// getPostableTelegramData - Returns slices containing 20~ locations each +// to send as separate messages. map[location][]locationsofinterest +func getPostableTelegramData() map[string][]string { + // Create our return map + groups := make(map[string][]string, 0) + + // If no locations, lets return empty map + if len(newLocations.Items) == 0 { + return groups + } + + for location, items := range newLocations.Items { + // Create out output buffer per location + rows := make([]string, 0) + + // Foreach item, create the output text based off the item + for _, item := range items { + rows = append(rows, getTelegramRow(item)) + + // Make sure to create a new slice if we have >100 to send as a different message + if len(rows) > TelegramMaxPerPost { + groups[location] = append(groups[location], strings.Join(rows, "\n\n")) + rows = make([]string, 0) + } + } + + // If we have less than 100, append any more before next location + if len(rows) > 0 { + groups[location] = append(groups[location], strings.Join(rows, "\n\n")) + } + } + + return groups +} + +// getTelegramRow Format the string to a tidy string for the interwebs +func getTelegramRow(item ApiItem) string { + return fmt.Sprintf("%s\n%s\n%s", + telegramEscape(item.EventName), telegramEscape(item.Location.Address), telegramEscape(item.getDateString())) +} + +func telegramEscape(s string) string { + return strings.ReplaceAll( + strings.ReplaceAll( + strings.ReplaceAll( + s, + "&", + "&", + ), + ">", + ">", + ), + "<", + "<", + ) +}