diff --git a/.env.template b/.env.template index 1f0b0c7..b07ca8a 100644 --- a/.env.template +++ b/.env.template @@ -1,2 +1,3 @@ SN_AUTH_COOKIE= DISCORD_WEBHOOK= +DISCORD_TOKEN= diff --git a/discord.go b/discord.go index 6495b60..1ac7599 100644 --- a/discord.go +++ b/discord.go @@ -6,12 +6,15 @@ import ( "log" "net/http" + "github.com/bwmarrin/discordgo" "github.com/joho/godotenv" "github.com/namsral/flag" ) var ( DiscordWebhook string + DiscordToken string + DiscordClient *discordgo.Session ) type DiscordEmbedFooter struct { @@ -37,10 +40,45 @@ func init() { log.Fatal("Error loading .env file") } flag.StringVar(&DiscordWebhook, "DISCORD_WEBHOOK", "", "Webhook to send logs to discord") + flag.StringVar(&DiscordToken, "DISCORD_TOKEN", "", "Discord bot token") flag.Parse() if DiscordWebhook == "" { log.Fatal("DISCORD_WEBHOOK not set") } + if DiscordToken == "" { + log.Fatal("DISCORD_TOKEN not set") + } + initBot() +} + +func initBot() { + var err error + DiscordClient, err = discordgo.New(DiscordToken) + if err != nil { + log.Fatal("error creating discord session:", err) + } + DiscordClient.AddHandler(func(s *discordgo.Session, event *discordgo.Ready) { + log.Println("Logged in as", event.User.Username) + }) + DiscordClient.AddHandler(onMessage) + DiscordClient.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsMessageContent + err = DiscordClient.Open() + if err != nil { + log.Fatal("error opening connection to discord: ", err, " -- Is your token correct?") + } +} + +func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) { + // Ignore all messages created by the bot itself + if m.Author.ID == s.State.User.ID { + return + } + id, err := ParseHackerNewsLink(m.Content) + if err != nil { + return + } + story := FetchStoryById(id) + PostStoryToStackerNews(&story) } func SendEmbedToDiscord(embed DiscordEmbed) { diff --git a/go.mod b/go.mod index 1b87bf2..9b1b253 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,11 @@ require ( ) require ( + github.com/bwmarrin/discordgo v0.27.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 39d5eba..2099a9b 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,12 @@ +github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= +github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs= @@ -16,6 +20,14 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hn.go b/hn.go index f200536..4039f1e 100644 --- a/hn.go +++ b/hn.go @@ -2,9 +2,12 @@ package main import ( "encoding/json" + "errors" "fmt" "log" "net/http" + "regexp" + "strconv" ) type ItemID = int @@ -23,11 +26,13 @@ type Story struct { var ( HackerNewsUrl string HackerNewsFirebaseUrl string + HackerNewsLinkRegexp *regexp.Regexp ) func init() { HackerNewsUrl = "https://news.ycombinator.com" HackerNewsFirebaseUrl = "https://hacker-news.firebaseio.com/v0" + HackerNewsLinkRegexp = regexp.MustCompile(`(?:https?:\/\/)?news\.ycombinator\.com\/item\?id=([0-9]+)`) } func FetchHackerNewsTopStories() []Story { @@ -79,6 +84,19 @@ func FetchStoryById(id ItemID) Story { return story } +func ParseHackerNewsLink(link string) (ItemID, error) { + match := HackerNewsLinkRegexp.FindStringSubmatch(link) + if len(match) == 0 { + return -1, errors.New("input is not a hacker news link") + } + id, err := strconv.Atoi(match[1]) + if err != nil { + // this should never happen + panic(err) + } + return id, nil +} + func HackerNewsUserLink(user string) string { return fmt.Sprintf("%s/user?id=%s", HackerNewsUrl, user) }