mirror of
https://github.com/idanoo/NZCovidBot
synced 2025-07-01 03:02:15 +00:00
Split based on location
This commit is contained in:
parent
83b1a3d974
commit
aeae0ef8ee
6 changed files with 129 additions and 67 deletions
12
CHANGELOG.md
Normal file
12
CHANGELOG.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 1.1
|
||||
- Split messages based on location
|
||||
- Added systemd service
|
||||
|
||||
## 1.0
|
||||
- Reworked to use API
|
||||
- Stores lastPoll / lastUpdated in lastUpdated.txt to keep track
|
||||
- Only posts if PublishedDate > lastUpdated timestamp
|
||||
- Removed twitter + git code
|
||||
- Added more code comments
|
|
@ -1,5 +1,6 @@
|
|||
# NZCovidBot
|
||||
Pulls data from Ministry of Health API and parse into Discord and Slack webhooks.
|
||||
|
||||
### About
|
||||
After the twitterbot @nzcovidlocs shut down, I decided to try a different approach, instead of scraping MoH's website, we originally parsed the raw CSV data.
|
||||
Since then the NZ Ministry of Health have released an API containing this data now. We are now using this https://api.integration.covid19.health.nz/locations/v1/current-locations-of-interest
|
||||
|
@ -16,4 +17,7 @@ Copy .env.example to .env and fill in the webhook URLs
|
|||
```
|
||||
go build -o nzcovidbot cmd/nzcovidbot/*.go
|
||||
./nzcovidbot
|
||||
sudo cp nzcovidbot.service /etc/systemd/system/nzcovidbot.service
|
||||
# Update user + location of repo in systemd file
|
||||
sudo systemctl daemon-reload && systemctl enable --now nzcovidbot.service
|
||||
```
|
||||
|
|
|
@ -13,23 +13,29 @@ import (
|
|||
|
||||
const API_ENDPOINT = "https://api.integration.covid19.health.nz/locations/v1/current-locations-of-interest"
|
||||
|
||||
var newLocations ApiResponse
|
||||
var newLocations PostResponse
|
||||
|
||||
// Response from MoH API
|
||||
type ApiResponse struct {
|
||||
Items []ApiItem `json:"items"`
|
||||
}
|
||||
|
||||
// PostResponse - Above items ordered by location
|
||||
type PostResponse struct {
|
||||
Items map[string][]ApiItem `json:"items"`
|
||||
}
|
||||
|
||||
type ApiItem struct {
|
||||
EventID string `json:"eventId"`
|
||||
EventName string `json:"eventName"`
|
||||
StartDateTime time.Time `json:"startDateTime"`
|
||||
EndDateTime time.Time `json:"endDateTime"`
|
||||
PublicAdvice string `json:"publicAdvice"`
|
||||
VisibleInWebform bool `json:"visibleInWebform"`
|
||||
PublishedAt time.Time `json:"publishedAt"`
|
||||
// UpdatedAt time.Time `json:"updatedAt"` // Nullable
|
||||
ExposureType string `json:"exposureType"`
|
||||
Location struct {
|
||||
EventID string `json:"eventId"`
|
||||
EventName string `json:"eventName"`
|
||||
StartDateTime time.Time `json:"startDateTime"`
|
||||
EndDateTime time.Time `json:"endDateTime"`
|
||||
PublicAdvice string `json:"publicAdvice"`
|
||||
VisibleInWebform bool `json:"visibleInWebform"`
|
||||
PublishedAt time.Time `json:"publishedAt"`
|
||||
UpdatedAt interface{} `json:"updatedAt"`
|
||||
ExposureType string `json:"exposureType"`
|
||||
Location struct {
|
||||
Latitude string `json:"latitude"`
|
||||
Longitude string `json:"longitude"`
|
||||
Suburb string `json:"suburb"`
|
||||
|
@ -85,25 +91,30 @@ func getNewAPILocations() {
|
|||
}
|
||||
|
||||
// Re-init our apiRepsonse so we don't hold onto old locations!
|
||||
newItems := make([]ApiItem, 0)
|
||||
newItems := make(map[string][]ApiItem, 0)
|
||||
|
||||
// Iterate over the data and only find new locations
|
||||
for _, item := range locations.Items {
|
||||
if item.PublishedAt.Unix() > lastUpdated.Unix() {
|
||||
// Clone the item to put in our own lil slice
|
||||
copy := item
|
||||
newItems = append(newItems, copy)
|
||||
newItems[item.Location.City] = append(newItems[item.Location.City], copy)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to clear out the previous list and append new data
|
||||
newLocations = ApiResponse{}
|
||||
newLocations.Items = newItems
|
||||
// Make sure to clear out the previous list and append new data in a map based on location
|
||||
newLocations = PostResponse{}
|
||||
newLocations.Items = make(map[string][]ApiItem, 0)
|
||||
|
||||
// Order by StartDate
|
||||
sort.Slice(newLocations.Items, func(i, j int) bool {
|
||||
return newLocations.Items[i].StartDateTime.Before(newLocations.Items[j].StartDateTime)
|
||||
})
|
||||
for mapKey, mapItems := range newItems {
|
||||
// Add location to our newLocations map
|
||||
newLocations.Items[mapKey] = mapItems
|
||||
|
||||
// Order by StartDate
|
||||
sort.Slice(newLocations.Items[mapKey], func(i, j int) bool {
|
||||
return newLocations.Items[mapKey][i].StartDateTime.Before(newLocations.Items[mapKey][j].StartDateTime)
|
||||
})
|
||||
}
|
||||
|
||||
// If new items, post it!
|
||||
if len(newLocations.Items) > 0 {
|
||||
|
@ -147,5 +158,5 @@ func (item ApiItem) getDateString() string {
|
|||
dateFormat := "Mon 2 Jan, 03:04PM"
|
||||
timeFormat := "03:04PM"
|
||||
|
||||
return st.Format(dateFormat) + " - " + et.Format(timeFormat)
|
||||
return st.Local().Format(dateFormat) + " - " + et.Local().Format(timeFormat)
|
||||
}
|
||||
|
|
|
@ -20,54 +20,71 @@ func postToDiscord() {
|
|||
}
|
||||
|
||||
for _, discordWebhook := range DiscordWebhooks {
|
||||
for _, postableData := range postableDiscordData {
|
||||
if discordWebhook != "" {
|
||||
tokenParts := strings.Split(discordWebhook, "/")
|
||||
len := len(tokenParts)
|
||||
if discordWebhook != "" {
|
||||
// Build discord request
|
||||
tokenParts := strings.Split(discordWebhook, "/")
|
||||
len := len(tokenParts)
|
||||
webhook, err := disgohook.NewWebhookClientByToken(nil, nil, tokenParts[len-2]+"/"+tokenParts[len-1])
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Build discord request
|
||||
webhook, err := disgohook.NewWebhookClientByToken(nil, nil, tokenParts[len-2]+"/"+tokenParts[len-1])
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
// Build message and send for each location
|
||||
for location, postableData := range postableDiscordData {
|
||||
for _, post := range postableData {
|
||||
|
||||
// Send discord message
|
||||
_, err = webhook.SendEmbeds(api.NewEmbedBuilder().
|
||||
SetTitle("*" + location + "*").
|
||||
SetDescription(post).
|
||||
Build(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Send discord message
|
||||
_, err = webhook.SendEmbeds(api.NewEmbedBuilder().
|
||||
SetDescription(postableData).
|
||||
Build(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getPostableDiscordData - Returns slices containing 20~ locations each
|
||||
// to send as separate messages
|
||||
func getPostableDiscordData() []string {
|
||||
// Create our slice of groups
|
||||
groups := make([]string, 0)
|
||||
// to send as separate messages. map[location][]locationsofinterest
|
||||
func getPostableDiscordData() 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
|
||||
}
|
||||
|
||||
rows := make([]string, 0)
|
||||
for _, item := range newLocations.Items {
|
||||
rows = append(rows, getDiscordRow(item))
|
||||
for location, items := range newLocations.Items {
|
||||
// Create out output buffer per location
|
||||
rows := make([]string, 0)
|
||||
|
||||
if len(rows) > 20 {
|
||||
groups = append(groups, strings.Join(rows, "\n"))
|
||||
rows = make([]string, 0)
|
||||
// Foreach item, create the output text based off the item
|
||||
for _, item := range items {
|
||||
rows = append(rows, getDiscordRow(item))
|
||||
|
||||
// Make sure to create a new slice if we have >20 to send as a different message
|
||||
if len(rows) > 20 {
|
||||
groups[location] = append(groups[location], strings.Join(rows, "\n"))
|
||||
rows = make([]string, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// If we have less than 20, append any more before next location
|
||||
if len(rows) > 0 {
|
||||
groups[location] = append(groups[location], strings.Join(rows, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
return append(groups, strings.Join(rows, "\n"))
|
||||
return groups
|
||||
}
|
||||
|
||||
// formatCsvDiscordRow Format the string to a tidy string for the interwebs
|
||||
|
|
|
@ -28,30 +28,35 @@ func postToSlack() {
|
|||
}
|
||||
|
||||
parts := strings.Split(webhook, "!")
|
||||
payload := slack.Payload{
|
||||
Text: "New Locations of Interest!",
|
||||
Username: "NZCovidTracker",
|
||||
Channel: "#" + parts[1],
|
||||
IconUrl: "https://www.skids.co.nz/wp-content/uploads/2020/08/download.png",
|
||||
Attachments: attachmentData,
|
||||
}
|
||||
|
||||
err := slack.Send(parts[0], "", payload)
|
||||
if len(err) > 0 {
|
||||
fmt.Printf("Wehbook: %s\nError: %s", webhook, err)
|
||||
for location, items := range attachmentData {
|
||||
payload := slack.Payload{
|
||||
Text: "*" + location + "*",
|
||||
Username: "NZCovidTracker",
|
||||
Channel: "#" + parts[1],
|
||||
IconUrl: "https://www.skids.co.nz/wp-content/uploads/2020/08/download.png",
|
||||
Attachments: items,
|
||||
}
|
||||
|
||||
err := slack.Send(parts[0], "", payload)
|
||||
if len(err) > 0 {
|
||||
fmt.Printf("Wehbook: %s\nError: %s", webhook, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds new rows to a slice for slack
|
||||
func getPostableSlackData() []slack.Attachment {
|
||||
rows := make([]slack.Attachment, 0)
|
||||
func getPostableSlackData() map[string][]slack.Attachment {
|
||||
rows := make(map[string][]slack.Attachment, 0)
|
||||
if len(newLocations.Items) == 0 {
|
||||
return rows
|
||||
}
|
||||
|
||||
for _, item := range newLocations.Items {
|
||||
rows = append(rows, getSlackRow(item))
|
||||
for location, items := range newLocations.Items {
|
||||
for _, item := range items {
|
||||
rows[location] = append(rows[location], getSlackRow(item))
|
||||
}
|
||||
}
|
||||
|
||||
return rows
|
||||
|
|
13
nzcovidbot.service
Normal file
13
nzcovidbot.service
Normal file
|
@ -0,0 +1,13 @@
|
|||
[Unit]
|
||||
Description=NZCovidBot
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/root/NZCovidBot
|
||||
ExecStart=/root/NZCovidBot/nzcovidbot
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
Loading…
Add table
Add a link
Reference in a new issue