Rework to use API instead of CSVs

This commit is contained in:
Daniel Mason 2022-01-02 12:01:59 +13:00
parent b73a6b7cae
commit 4937ec7253
13 changed files with 290 additions and 675 deletions

View file

@ -1,10 +1,3 @@
SOURCE_REPO=https://github.com/minhealthnz/nz-covid-data.git // Source repo
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)
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_TOKEN_SECRET=

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
.env
tmp
lastUpdated.txt

View file

@ -1,15 +1,11 @@
# NZCovidBot
Pull data from Github and parse new locations and fire them off to any configured endpoints (Slack, Discord, Twitter(untested)).
It currently posts if a new row is added or a name/date changes from a previous line.
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, lets parse the raw data!
https://github.com/minhealthnz/nz-covid-data/tree/main/locations-of-interest/august-2021
It will clone ministry of healths git repo and poll every minute for updates to their raw CSV
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
## Config
Copy .env.example to .env and fill in the blanks.
Copy .env.example to .env and fill in the webhook URLs
### Run locally
```

View file

@ -24,17 +24,6 @@ func main() {
// Fetch slack webhooks
nzcovidbot.SlackWebhooks = strings.Split(os.Getenv("SLACK_WEBHOOKS"), ",")
// Fetch twitter keys
nzcovidbot.TwitterCreds = nzcovidbot.TwitterCredentials{
ConsumerKey: os.Getenv("TWITTER_CONSUMER_KEY"),
ConsumerSecret: os.Getenv("TWITTER_CONSUMER_SECRET"),
AccessToken: os.Getenv("TWITTER_ACCESS_TOKEN"),
AccessTokenSecret: os.Getenv("TWITTER_ACCESS_TOKEN_SECRET"),
}
// Git repo URL
nzcovidbot.Repository = os.Getenv("SOURCE_REPO")
// Boot up listeners / main loop
nzcovidbot.Lesgoooo()
}

7
go.mod
View file

@ -5,13 +5,12 @@ go 1.16
require (
github.com/DisgoOrg/disgohook v1.4.3
github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960
github.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e
github.com/dghubble/oauth1 v0.7.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 // indirect
github.com/go-git/go-git/v5 v5.4.2
github.com/joho/godotenv v1.3.0
github.com/parnurzeal/gorequest v0.2.16 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d
golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect
moul.io/http2curl v1.0.0 // indirect
)

94
go.sum
View file

@ -4,141 +4,47 @@ github.com/DisgoOrg/log v1.1.0 h1:a6hLfVSDuTFJc5AKQ8FDYQ5TASnwk3tciUyXThm1CR4=
github.com/DisgoOrg/log v1.1.0/go.mod h1:Qihgz6fax3JCfuO7vxVavL0LyHS0sUdQ9OmykQ2fiQs=
github.com/DisgoOrg/restclient v1.2.7 h1:A8ltFP+8O2q9ZTk/vRmlQRvH2cbcnaacUV2uXWFcONQ=
github.com/DisgoOrg/restclient v1.2.7/go.mod h1:2pc/htya/5kjxvWNYya98sb8B4mexobxmWvhTiWPt94=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960 h1:MIEURpsIpyLyy+dZ+GnL8T5P49Tco0ik9cYaUQNnAxE=
github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960/go.mod h1:97O1qkjJBHSSaWJxsTShRIeFy0HWiygk+jnugO9aX3I=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e h1:o0sI/cfhAXdtAbiIVeTd4hmwtwgs4cQFwRBhmbx8AKY=
github.com/dghubble/go-twitter v0.0.0-20210609183100-2fdbf421508e/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE=
github.com/dghubble/oauth1 v0.7.0 h1:AlpZdbRiJM4XGHIlQ8BuJ/wlpGwFEJNnB4Mc+78tA/w=
github.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=
github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=
github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
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/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
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/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d h1:xQcF7b7cZLWZG/+7A4G7un1qmEDYHIvId9qxRS1mZMs=
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d/go.mod h1:BzSc3WEF8R+lCaP5iGFRxd5kIXy4JKOZAwNe1w0cdc0=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=

151
internal/nzcovidbot/api.go Normal file
View file

@ -0,0 +1,151 @@
package nzcovidbot
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"sort"
"time"
)
const API_ENDPOINT = "https://api.integration.covid19.health.nz/locations/v1/current-locations-of-interest"
var newLocations ApiResponse
type ApiResponse struct {
Items []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 {
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
Suburb string `json:"suburb"`
City string `json:"city"`
Address string `json:"address"`
} `json:"location"`
}
// fetchAPILocations - Return struct of API response
func fetchAPILocations() (ApiResponse, error) {
var apiResponse ApiResponse
// Build HTTP Client and create request
client := &http.Client{}
req, err := http.NewRequest("GET", API_ENDPOINT, nil)
if err != nil {
return apiResponse, err
}
// Set user-agent info
req.Header.Set("User-Agent", "NZCovidBot/1.0 (https://m2.nz)")
// Fire off the request
resp, err := client.Do(req)
if err != nil {
return apiResponse, err
}
defer resp.Body.Close()
// Read body response
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return apiResponse, err
}
// Unmarshal JSON into Go struct
err = json.Unmarshal(body, &apiResponse)
return apiResponse, err
}
// getNewAPILocations - Gets all locations and triggers posts
func getNewAPILocations() {
// Set lastUpdate time at the start of the request so we don't miss any locations
// posted right after we poll
newPollTime := time.Now()
// Pull latest data
locations, err := fetchAPILocations()
if err != nil {
log.Printf("Error fetching API Locations %s", err)
return
}
// Re-init our apiRepsonse so we don't hold onto old locations!
newItems := make([]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)
}
}
// Make sure to clear out the previous list and append new data
newLocations = ApiResponse{}
newLocations.Items = newItems
// Order by StartDate
sort.Slice(newLocations.Items, func(i, j int) bool {
return newLocations.Items[i].StartDateTime.Before(newLocations.Items[j].StartDateTime)
})
// If new items, post it!
if len(newLocations.Items) > 0 {
postTheUpdates()
}
updateLastUpdated(newPollTime)
}
// updateLastUpdated - Creates/Updates lastUpdated.txt
func updateLastUpdated(newUpdated time.Time) {
// Make sure to update the global var for next poll
lastUpdated = newUpdated
// Open file in truncate/append mode
f, err := os.OpenFile("lastUpdated.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
log.Println(err)
return
}
// Write data
data := []byte(fmt.Sprintf("%d", lastUpdated.Unix()))
_, err = f.Write(data)
if err != nil {
log.Println(err)
return
}
// Close file so we can reopen next time
if err := f.Close(); err != nil {
log.Println(err)
}
}
// getDateString - Returns Date + StartTime + EndTime
func (item ApiItem) getDateString() string {
st := item.StartDateTime
et := item.EndDateTime
dateFormat := "Mon 2 Jan, 03:04PM"
timeFormat := "03:04PM"
return st.Format(dateFormat) + " - " + et.Format(timeFormat)
}

View file

@ -1,315 +0,0 @@
package nzcovidbot
import (
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"log"
"net/url"
"os"
"regexp"
"sort"
"strings"
"time"
"github.com/ashwanthkumar/slack-go-webhook"
)
// Slice of updated located
type UpdatedLocations struct {
Locations []UpdatedRow
}
// Updated data
type UpdatedRow struct {
FromDate time.Time `json:"FromDate"` // Start date
EndDate time.Time `json:"EndDate"` // End date
LocationName string `json:"LocationName"` // Location Name
LocationAddress string `json:"LocationAddress"` // Location Address
DiscordData string `json:"-"` // Formatted Row data
TwitterData string `json:"-"` // Formatted Row data
SlackData slack.Attachment `json:"-"` // Formatted Row data
}
// Struct of updated locations
var updatedLocations UpdatedLocations
// cache of [exposureID]row of row data
var rowCache map[string]UpdatedRow
// parseCsvRow Build into struct for output later
func parseCsvRow(data string) {
c, st, et, err := parseRawRowData(data)
if err != nil {
return
}
if len(c) < 5 {
log.Printf("Invalid line. Skipping")
return
}
if rowHasChanged(c[4], st, et, c[2], c[3]) {
newRow := UpdatedRow{
FromDate: st,
EndDate: et,
LocationName: c[2],
LocationAddress: c[3],
DiscordData: formatCsvDiscordRow(c),
TwitterData: formatCsvTwitterRow(c),
SlackData: formatCsvSlackRow(c),
}
// Update row cache! [exposureId]UpdatedRow
rowCache[c[4]] = newRow
// Append row data
updatedLocations.Locations = append(updatedLocations.Locations, newRow)
}
}
// rowHasChanged - Determine if row has actually changed based on raw data
func rowHasChanged(exposureId string, startTime time.Time, endTime time.Time, locationName string, locationAddress string) bool {
val, exists := rowCache[exposureId]
if !exists {
log.Printf("exposureId %s is new. Adding to cache", exposureId)
return true
}
if val.FromDate.Unix() != startTime.Unix() {
log.Printf("StartDate Change for %s from %s to %s", exposureId, val.FromDate.String(), startTime.String())
return true
}
if val.EndDate.Unix() != endTime.Unix() {
log.Printf("EndDate Change for %s from %s to %s", exposureId, val.EndDate.String(), endTime.String())
return true
}
if !strings.EqualFold(val.LocationName, locationName) {
log.Printf("LocationName Change for %s from %s to %s", exposureId, val.LocationName, locationName)
return true
}
// if !strings.EqualFold(val.LocationAddress, locationAddress) {
// log.Printf("LocationAddress Change for %s from %s to %s", exposureId, val.LocationAddress, locationAddress)
// return true
// }
return false
}
// loadRepoIntoCache - reads all CSV data and parses the rows into our cache
func loadRepoIntoCache(repoLocation string) {
// Init our cache!
rowCache = make(map[string]UpdatedRow)
// Load cache file. ELSE load files.
folders, err := ioutil.ReadDir(repoLocation + "/locations-of-interest")
if err != nil {
log.Fatal(err)
}
// /august-2021
for _, f := range folders {
if f.IsDir() {
files, err := ioutil.ReadDir(repoLocation + "/locations-of-interest/" + f.Name())
if err != nil {
log.Fatal(err)
}
// august-2021/locations-of-interest.csv
for _, x := range files {
fullLocation := repoLocation + "/locations-of-interest/" + f.Name() + "/" + x.Name()
if strings.HasSuffix(fullLocation, ".csv") {
loadRowsIntoCache(fullLocation)
}
}
}
}
log.Printf("Successfully populated cache with %d entries", len(rowCache))
}
func loadRowsIntoCache(filePath string) {
// Open the file
csvfile, err := os.Open(filePath)
if err != nil {
log.Fatal(err)
}
defer csvfile.Close()
// Parse the file
r := csv.NewReader(csvfile)
// Iterate through the records
i := 0
for {
// Read each record from csv
row, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
continue
}
// Skip header row
if i == 0 {
i++
continue
}
// Parse into our required format
c := make([]string, 0)
c = append(c, row...)
st, et, err := parseRowTimes(c[4], c[5])
if err != nil {
continue
}
// Build object
newRow := UpdatedRow{
FromDate: st,
EndDate: et,
LocationName: c[1],
LocationAddress: c[2],
}
// Add to cache
rowCache[row[0]] = newRow
}
}
func orderRowDataByDate() {
sort.Slice(updatedLocations.Locations, func(i, j int) bool {
return updatedLocations.Locations[i].FromDate.Before(updatedLocations.Locations[j].FromDate)
})
}
// formatCsvDiscordRow Format the string to a tidy string for the interwebs
func formatCsvDiscordRow(c []string) string {
return fmt.Sprintf("**%s** %s on _%s_ - _%s_", c[2], c[3], c[0], c[1])
}
// formatCsvTwitterRow Format the string to a tidy string for the interwebs
func formatCsvTwitterRow(c []string) string {
return fmt.Sprintf("New Location: %s\n%s\n%s - %s\n#NZCovidTracker #NZCovid", c[2], c[3], c[0], c[1])
}
// formatCsvSlackRow Format the string to a tidy string for the interwebs
func formatCsvSlackRow(c []string) slack.Attachment {
url := getMapsLinkFromAddress(c[2], c[3])
name := stripDateFromName(c[2])
dateRange := fmt.Sprintf("%s - %s", c[0], c[1])
attachment := slack.Attachment{
Title: &name,
TitleLink: &url,
Text: &dateRange,
}
return attachment
}
// getMapsLinkFromAddress hyperlink gmaps
func getMapsLinkFromAddress(name string, address string) string {
return fmt.Sprintf("https://www.google.com/maps/search/?api=1&query=%s", url.QueryEscape(name+", "+address))
}
// stripDateFromName if theres a date at the end - remove it!
func stripDateFromName(name string) string {
re := regexp.MustCompile(`\d{1,2}/\d{1,2}/\d{2,4}`)
submatchall := re.FindAllString(name, -1)
for _, element := range submatchall {
name = strings.Replace(name, element, "", 1)
break
}
return strings.TrimSpace(name)
}
// Returns []string of parsed data.. starttime, endtime, name, address, ID
func parseRawRowData(data string) ([]string, time.Time, time.Time, error) {
output := make([]string, 0)
r := csv.NewReader(strings.NewReader(data))
r.Comma = ','
fields, err := r.Read()
if err != nil {
fmt.Println(err)
return output, time.Now(), time.Now(), err
}
c := make([]string, 0)
c = append(c, fields...)
if len(c) < 5 {
// Add helper in case someone somehow does something wrong
return output, time.Now(), time.Now(), err
}
st, et, err := parseRowTimes(c[4], c[5])
starttime := st.Format("Monday 2 Jan, 3:04PM")
endtime := et.Format("3:04PM")
return append(output, starttime, endtime, c[1], c[2], c[0]), st, et, err
}
func parseRowTimes(startString string, endString string) (time.Time, time.Time, error) {
st, err := time.Parse("2/01/2006, 3:04 pm", startString)
if err != nil {
log.Print(err)
st, err = time.Parse("2006-01-02 15:04:05", startString)
if err != nil {
log.Print(err)
return time.Now(), time.Now(), err
}
}
et, err := time.Parse("2/01/2006, 3:04 pm", endString)
if err != nil {
log.Print(err)
et, err = time.Parse("2006-01-02 15:04:05", endString)
if err != nil {
log.Print(err)
return time.Now(), time.Now(), err
}
}
return st, et, nil
}
func getPostableDiscordData() []string {
groups := make([]string, 0)
if len(updatedLocations.Locations) == 0 {
return groups
}
rows := make([]string, 0)
for _, location := range updatedLocations.Locations {
rows = append(rows, location.DiscordData)
if len(rows) > 20 {
groups = append(groups, strings.Join(rows, "\n"))
rows = make([]string, 0)
}
}
return append(groups, strings.Join(rows, "\n"))
}
func getPostableSlackData() []slack.Attachment {
rows := make([]slack.Attachment, 0)
if len(updatedLocations.Locations) == 0 {
return rows
}
for _, location := range updatedLocations.Locations {
rows = append(rows, location.SlackData)
}
return rows
}

View file

@ -1,8 +1,10 @@
package nzcovidbot
import (
"fmt"
"log"
"strings"
"time"
"github.com/DisgoOrg/disgohook"
"github.com/DisgoOrg/disgohook/api"
@ -11,29 +13,66 @@ import (
// Slice of discord webhooks
var DiscordWebhooks []string
func postToDiscord(webhookString string, msg string) {
if webhookString == "" {
func postToDiscord() {
postableDiscordData := getPostableDiscordData()
if len(postableDiscordData) == 0 {
return
}
tokenParts := strings.Split(webhookString, "/")
len := len(tokenParts)
webhook, err := disgohook.NewWebhookClientByToken(nil, nil, tokenParts[len-2]+"/"+tokenParts[len-1])
if err != nil {
log.Print(err)
return
}
for _, discordWebhook := range DiscordWebhooks {
for _, postableData := range postableDiscordData {
if discordWebhook != "" {
tokenParts := strings.Split(discordWebhook, "/")
len := len(tokenParts)
_, err = webhook.SendEmbeds(api.NewEmbedBuilder().
SetDescription(msg).
Build(),
)
if err != nil {
log.Print(err)
return
}
// Build discord request
webhook, err := disgohook.NewWebhookClientByToken(nil, nil, tokenParts[len-2]+"/"+tokenParts[len-1])
if err != nil {
log.Print(err)
return
}
if err != nil {
log.Print(err)
// 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)
if len(newLocations.Items) == 0 {
return groups
}
rows := make([]string, 0)
for _, item := range newLocations.Items {
rows = append(rows, getDiscordRow(item))
if len(rows) > 20 {
groups = append(groups, strings.Join(rows, "\n"))
rows = make([]string, 0)
}
}
return append(groups, strings.Join(rows, "\n"))
}
// formatCsvDiscordRow 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())
}

View file

@ -1,144 +0,0 @@
package nzcovidbot
import (
"log"
"os"
"strings"
"github.com/go-git/go-git/v5"
"github.com/waigani/diffparser"
)
// Repo URL
var gitRepo *git.Repository
// Location to store tmp data
var tmpDirectory = "./tmp"
// loadRepo Load repo into gitRepo var
func loadRepo(repository string) {
r, err := git.PlainOpen(tmpDirectory + "/repo")
if err != nil {
if err.Error() == "repository does not exist" {
r = cloneRepo(repository)
} else {
log.Fatal(err)
}
}
// Preload cache data of current rows
loadRepoIntoCache(tmpDirectory + "/repo")
gitRepo = r
}
// cloneRepo Clone git repo
func cloneRepo(repository string) *git.Repository {
if _, err := os.Stat(tmpDirectory); os.IsNotExist(err) {
err = os.Mkdir(tmpDirectory, 0755)
if err != nil {
log.Fatal(err)
}
}
r, err := git.PlainClone(tmpDirectory+"/repo", false, &git.CloneOptions{
URL: repository,
Progress: os.Stdout,
})
if err != nil {
log.Fatal(err)
}
log.Println("Succesfully cloned repo")
return r
}
func checkForUpdates() {
// Fetch updates from remote
pullOptions := git.PullOptions{
RemoteName: "origin",
}
// Get current commit hash PRE PULL
currentHead, err := gitRepo.Head()
if err != nil {
log.Printf("Err getting head: %s", err)
return
}
currentHash := currentHead.Hash()
// Pull latest changes if exist
worktree, err := gitRepo.Worktree()
if err != nil {
log.Printf("Err getting worktree: %s", err)
}
err = worktree.Pull(&pullOptions)
if err != nil {
if err == git.NoErrAlreadyUpToDate {
log.Println("No updates")
return
} else {
log.Printf("Err pulling: %s", err)
return
}
}
// Get current commit hash POST PULL
head, err := gitRepo.Head()
if err != nil {
log.Printf("Err getting new head: %s", err)
return
}
// Get latest commit
latestCommit, err := gitRepo.CommitObject(head.Hash())
if err != nil {
log.Printf("Err getting latest commit: %s", err)
return
}
// Get current commit
currentCommit, err := gitRepo.CommitObject(currentHash)
if err != nil {
log.Printf("Err getting new commit: %s", err)
return
}
// Get patch of changes
patch, err := currentCommit.Patch(latestCommit)
if err != nil {
log.Printf("Err getting change patch: %s", err)
return
}
// Parse git diff
diff, err := diffparser.Parse(patch.String())
if err != nil {
log.Printf("Err parsing diff: %s", err)
return
}
// Loop through file changes
for _, file := range diff.Files {
if strings.HasSuffix(file.NewName, ".csv") {
if strings.HasPrefix(file.NewName, "locations-of-interest/") {
for _, hunk := range file.Hunks {
newRange := hunk.WholeRange
for _, line := range newRange.Lines {
if strings.Contains(line.Content, "Start,End,Advice") {
continue
}
if line.Mode == diffparser.ADDED {
parseCsvRow(line.Content)
}
}
}
}
}
}
// SEND IT o/---->
postTheUpdates()
}

View file

@ -2,14 +2,21 @@ package nzcovidbot
import (
"fmt"
"io/ioutil"
"log"
"os"
"strconv"
"time"
)
var Repository string
// Time of last succesful poll
var lastUpdated time.Time
// Main func
func Lesgoooo() {
// Setup repo stuff
loadRepo(Repository)
// Set last updated poll time!
lastUpdated = getLastPollTime()
log.Printf("Using last updated time: %s", lastUpdated.Local())
// Create chan to end timer
endTicker := make(chan bool)
@ -17,8 +24,8 @@ func Lesgoooo() {
// Timer to run every minute
minuteTicker := time.NewTicker(time.Duration(60) * time.Second)
// Initial check on load
go checkForUpdates()
// Initial poll check on load
go getNewAPILocations()
for {
select {
@ -27,34 +34,39 @@ func Lesgoooo() {
return
case <-minuteTicker.C:
// Check for updates
go checkForUpdates()
go getNewAPILocations()
}
}
}
// getLastPollTime - If run previously, get last TS, otherwise Now()
func getLastPollTime() time.Time {
// Set default of *now* if never run so we don't spam everything
lastUpdated = time.Now()
// Load up last-polled date if set
file, err := os.Open("lastUpdated.txt")
if err == nil {
b, err := ioutil.ReadAll(file)
if err != nil {
log.Printf("Unable to read lastUpdated.txt: %s", err)
}
i, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
log.Printf("Unable to read lastUpdated.txt: %s", err)
}
lastUpdated = time.Unix(i, 0)
}
return lastUpdated
}
func postTheUpdates() {
// Lets reshuffle our structured data a bit (Exposure Date ASC)
orderRowDataByDate()
// Twitter
go postToTwitter()
// Slack
go postToSlack()
// Discord
postableDiscordData := getPostableDiscordData()
if len(postableDiscordData) == 0 {
return
}
for _, discordWebhook := range DiscordWebhooks {
for _, postableData := range postableDiscordData {
go postToDiscord(discordWebhook, postableData)
time.Sleep(500 * time.Millisecond)
}
}
// Clear out posted data!
updatedLocations = UpdatedLocations{}
go postToDiscord()
}

View file

@ -2,6 +2,7 @@ package nzcovidbot
import (
"fmt"
"net/url"
"strings"
"github.com/ashwanthkumar/slack-go-webhook"
@ -10,6 +11,7 @@ import (
// Slack webhook#channel
var SlackWebhooks []string
// Send slack request
func postToSlack() {
if len(SlackWebhooks) == 0 {
return
@ -40,3 +42,36 @@ func postToSlack() {
}
}
}
// Adds new rows to a slice for slack
func getPostableSlackData() []slack.Attachment {
rows := make([]slack.Attachment, 0)
if len(newLocations.Items) == 0 {
return rows
}
for _, item := range newLocations.Items {
rows = append(rows, getSlackRow(item))
}
return rows
}
// getSlackRow - Get slack attachment row
func getSlackRow(item ApiItem) slack.Attachment {
url := getMapsLinkFromAddress(item.EventName, item.Location.Address)
dateRange := item.getDateString()
attachment := slack.Attachment{
Title: &item.EventName,
TitleLink: &url,
Text: &dateRange,
}
return attachment
}
// getMapsLinkFromAddress hyperlink gmaps
func getMapsLinkFromAddress(name string, address string) string {
return fmt.Sprintf("https://www.google.com/maps/search/?api=1&query=%s", url.QueryEscape(name+", "+address))
}

View file

@ -1,46 +0,0 @@
package nzcovidbot
import (
"log"
"time"
"github.com/dghubble/go-twitter/twitter"
"github.com/dghubble/oauth1"
)
type TwitterCredentials struct {
ConsumerKey string
ConsumerSecret string
AccessToken string
AccessTokenSecret string
}
var TwitterCreds TwitterCredentials
func postToTwitter() {
if TwitterCreds.AccessTokenSecret == "" {
return
}
if len(updatedLocations.Locations) == 0 {
return
}
config := oauth1.NewConfig(TwitterCreds.ConsumerKey, TwitterCreds.ConsumerSecret)
token := oauth1.NewToken(TwitterCreds.AccessToken, TwitterCreds.AccessTokenSecret)
httpClient := config.Client(oauth1.NoContext, token)
// Twitter client
client := twitter.NewClient(httpClient)
// Send a Tweet
for _, row := range updatedLocations.Locations {
_, _, err := client.Statuses.Update(row.TwitterData, nil)
if err != nil {
log.Print(err)
}
// Lets not ratelimit ourselves :upsidedownsmiley:
time.Sleep(1 * time.Second)
}
}