mirror of
https://github.com/nikoksr/notify.git
synced 2025-03-17 20:57:58 +02:00
feat(service): Add Reddit service (#471)
This commit is contained in:
parent
f1cc0416c7
commit
faa82fdf46
@ -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: |
|
||||
|
2
go.mod
2
go.mod
@ -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
|
||||
|
9
go.sum
9
go.sum
@ -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=
|
||||
@ -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=
|
||||
@ -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=
|
||||
@ -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=
|
||||
@ -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=
|
||||
@ -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=
|
||||
|
16
service/reddit/README.md
Normal file
16
service/reddit/README.md
Normal file
@ -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 `http://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
service/reddit/doc.go
Normal file
37
service/reddit/doc.go
Normal file
@ -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
service/reddit/mock_reddit_message_client.go
Normal file
53
service/reddit/mock_reddit_message_client.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Code generated by mockery v2.15.0. DO NOT EDIT.
|
||||
|
||||
package reddit
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
v2reddit "github.com/vartanbeno/go-reddit/v2/reddit"
|
||||
)
|
||||
|
||||
// mockRedditMessageClient is an autogenerated mock type for the redditMessageClient type
|
||||
type mockRedditMessageClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Send provides a mock function with given fields: _a0, _a1
|
||||
func (_m *mockRedditMessageClient) Send(_a0 context.Context, _a1 *v2reddit.SendMessageRequest) (*v2reddit.Response, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 *v2reddit.Response
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v2reddit.SendMessageRequest) *v2reddit.Response); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v2reddit.Response)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v2reddit.SendMessageRequest) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTnewMockRedditMessageClient interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// newMockRedditMessageClient creates a new instance of mockRedditMessageClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func newMockRedditMessageClient(t mockConstructorTestingTnewMockRedditMessageClient) *mockRedditMessageClient {
|
||||
mock := &mockRedditMessageClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
87
service/reddit/reddit.go
Normal file
87
service/reddit/reddit.go
Normal file
@ -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
service/reddit/reddit_test.go
Normal file
76
service/reddit/reddit_test.go
Normal file
@ -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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user