Skip to content

Commit

Permalink
feat(service): Add Reddit service (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshm91 authored Nov 20, 2022
1 parent f1cc041 commit faa82fd
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Yes, please! Contributions of all kinds are very welcome! Feel free to check our
| [Plivo](https://www.plivo.com) | [service/plivo](service/plivo) | [plivo/plivo-go](https://github.com/plivo/plivo-go) | :heavy_check_mark: |
| [Pushover](https://pushover.net/) | [service/pushover](service/pushover) | [gregdel/pushover](https://github.com/gregdel/pushover) | :heavy_check_mark: |
| [Pushbullet](https://www.pushbullet.com) | [service/pushbullet](service/pushbullet) | [cschomburg/go-pushbullet](https://github.com/cschomburg/go-pushbullet) | :heavy_check_mark: |
| [Reddit](https://www.reddit.com) | [service/reddit](service/reddit) | [vartanbeno/go-reddit](https://github.com/vartanbeno/go-reddit) | :heavy_check_mark: |
| [RocketChat](https://rocket.chat) | [service/rocketchat](service/rocketchat) | [RocketChat/Rocket.Chat.Go.SDK](https://github.com/RocketChat/Rocket.Chat.Go.SDK) | :heavy_check_mark: |
| [SendGrid](https://sendgrid.com) | [service/sendgrid](service/sendgrid) | [sendgrid/sendgrid-go](https://github.com/sendgrid/sendgrid-go) | :heavy_check_mark: |
| [Slack](https://slack.com) | [service/slack](service/slack) | [slack-go/slack](https://github.com/slack-go/slack) | :heavy_check_mark: |
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ require (
maunium.net/go/mautrix v0.12.3
)

require github.com/vartanbeno/go-reddit/v2 v2.0.1

require (
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo=
github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20220903135808-56c5346a1a28 h1:OJe0G++TYGhE525XnkrF9KF15D1WtQdyrk19SFwRrKk=
Expand Down Expand Up @@ -108,6 +109,7 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down Expand Up @@ -233,6 +235,8 @@ github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWL
github.com/ttacon/libphonenumber v1.2.1/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M=
github.com/utahta/go-linenotify v0.5.0 h1:E1tJaB/XhqRY/iz203FD0MaHm10DjQPOq5/Mem2A3Gs=
github.com/utahta/go-linenotify v0.5.0/go.mod h1:KsvBXil2wx+ByaCR0e+IZKTbp4pDesc7yjzRigLf6pE=
github.com/vartanbeno/go-reddit/v2 v2.0.1 h1:P6ITpf5YHjdy7DHZIbUIDn/iNAoGcEoDQnMa+L4vutw=
github.com/vartanbeno/go-reddit/v2 v2.0.1/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand All @@ -243,7 +247,9 @@ golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20171115151908-9dfe39835686/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -255,10 +261,12 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20171101214715-fd80eb99c8f6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
Expand Down Expand Up @@ -296,6 +304,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
Expand Down
16 changes: 16 additions & 0 deletions service/reddit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Reddit Usage

Ensure that you have already navigated to your GOPATH and installed the following packages:

* `go get -u github.com/nikoksr/notify`

## Steps for Reddit notifications

These are general and very high level instructions

1. Log into Reddit create a new "script" type by visiting [here](https://www.reddit.com/prefs/apps/)
2. The "redirect uri" parameter doesn't matter in this case and can just be set to `https://localhost:8080`
2. Copy the *client id* and *client secret* for usage below
4. Now you should be good to use the code detailed in [doc.go](doc.go)

**NOTE**: You may have difficulties using your user's password if you have 2FA enabled. You can disable it by going [here](https://www.reddit.com/prefs/update/) but be aware of the security implications and ensure you have a strong password set.
37 changes: 37 additions & 0 deletions service/reddit/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Package reddit implements a Reddit notifier, allowing messages to be sent to multiple recipients
Usage:
package main
import (
"context"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/reddit"
)
func main() {
notifier := notify.New()
// Provide your Reddit app credentials and username/password
redditService := reddit.New("clientID", "clientSecret", "username", "password")
// Pass the usernames for where to send the messages
pushoverService.AddReceivers("User1", "User2")
// Tell our notifier to use the Reddit service. You can repeat the above process
// for as many services as you like and just tell the notifier to use them.
notifier.UseServices(redditService)
// Send a message
_ = notifier.Send(
context.Background(),
"Hello!",
"I am a bot written in Go!",
)
}
*/
package reddit
53 changes: 53 additions & 0 deletions service/reddit/mock_reddit_message_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 87 additions & 0 deletions service/reddit/reddit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Package reddit implements a Reddit notifier, allowing messages to be sent to multiple recipients
package reddit

import (
"context"
"crypto/tls"
"net/http"

"github.com/pkg/errors"
"github.com/vartanbeno/go-reddit/v2/reddit"
)

//go:generate mockery --name=redditMessageClient --output=. --case=underscore --inpackage
type redditMessageClient interface {
Send(context.Context, *reddit.SendMessageRequest) (*reddit.Response, error)
}

// Compile-time check to ensure that reddit.MessageService implements the redditMessageClient interface.
var _ redditMessageClient = new(reddit.MessageService)

// Reddit struct holds necessary data to communicate with the Reddit API.
type Reddit struct {
client redditMessageClient
recipients []string
}

// New returns a new instance of a Reddit notification service.
// For more information on obtaining client credentials:
//
// -> https://github.com/reddit-archive/reddit/wiki/OAuth2
func New(clientID, clientSecret, username, password string) (*Reddit, error) {
// Disable HTTP2 in http client
// Details: https://www.reddit.com/r/redditdev/comments/t8e8hc/getting_nothing_but_429_responses_when_using_go/i18yga2/
h := http.Client{
Transport: &http.Transport{
TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{},
},
}
rClient, err := reddit.NewClient(
reddit.Credentials{
ID: clientID,
Secret: clientSecret,
Username: username,
Password: password,
},
reddit.WithHTTPClient(&h),
reddit.WithUserAgent("github.com/nikoksr/notify"),
)
if err != nil {
return nil, errors.Wrap(err, "failed to instantiate base Reddit client")
}

r := &Reddit{
client: rClient.Message,
recipients: []string{},
}

return r, nil
}

// AddReceivers takes Reddit usernames and adds them to the internal recipient list. The Send method will send
// a given message to all of those users.
func (r *Reddit) AddReceivers(recipients ...string) {
r.recipients = append(r.recipients, recipients...)
}

// Send takes a message subject and a message body and sends them to all previously set recipients.
func (r *Reddit) Send(ctx context.Context, subject, message string) error {
for i := range r.recipients {
select {
case <-ctx.Done():
return ctx.Err()
default:
m := reddit.SendMessageRequest{
To: r.recipients[i],
Subject: subject,
Text: message,
}

_, err := r.client.Send(ctx, &m)
if err != nil {
return errors.Wrapf(err, "failed to send message to Reddit recipient '%s'", r.recipients[i])
}
}
}
return nil
}
76 changes: 76 additions & 0 deletions service/reddit/reddit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package reddit

import (
context "context"
"testing"

"github.com/pkg/errors"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/vartanbeno/go-reddit/v2/reddit"
)

func TestReddit_New(t *testing.T) {
t.Parallel()

assert := require.New(t)

service, err := New("id", "secret", "user", "password")
assert.NotNil(service)
assert.NoError(err)
}

func TestReddit_AddReceivers(t *testing.T) {
t.Parallel()

assert := require.New(t)

service, err := New("id", "secret", "user", "password")
assert.NotNil(service)
assert.NoError(err)

service.AddReceivers("")
assert.Len(service.recipients, 1)

service.AddReceivers("", "")
assert.Len(service.recipients, 3)
}

func TestReddit_Send(t *testing.T) {
t.Parallel()

assert := require.New(t)

service, err := New("id", "secret", "user", "password")
assert.NotNil(service)
assert.NoError(err)

// No receivers added
ctx := context.Background()
err = service.Send(ctx, "subject", "message")
assert.Nil(err)

// Test error response
mockClient := newMockRedditMessageClient(t)
mockClient.
On("Send", ctx, mock.AnythingOfType("*reddit.SendMessageRequest")).
Return(&reddit.Response{}, errors.New("some error"))

service.client = mockClient
service.AddReceivers("1234")
err = service.Send(ctx, "subject", "message")
assert.NotNil(err)
mockClient.AssertExpectations(t)

// Test success response
mockClient = newMockRedditMessageClient(t)
mockClient.
On("Send", ctx, mock.AnythingOfType("*reddit.SendMessageRequest")).
Return(&reddit.Response{}, nil)

service.client = mockClient
service.AddReceivers("5678")
err = service.Send(ctx, "subject", "message")
assert.Nil(err)
mockClient.AssertExpectations(t)
}

0 comments on commit faa82fd

Please sign in to comment.