mirror of
https://github.com/nikoksr/notify.git
synced 2024-12-02 08:51:50 +02:00
Merge branch 'main' into dependabot/go_modules/github.com/aws/aws-sdk-go-v2-1.3.4
This commit is contained in:
commit
f0e95bdca2
2
Makefile
2
Makefile
@ -27,7 +27,7 @@ test:
|
||||
|
||||
# gofumports and gci all go files
|
||||
fmt:
|
||||
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofumports -w "$$file"; done
|
||||
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofumpt -w "$$file"; done
|
||||
gci -w -local github.com/nikoksr/notify .
|
||||
.PHONY: fmt
|
||||
|
||||
|
@ -78,6 +78,7 @@ _ = notifier.Send(
|
||||
- *Telegram*
|
||||
- *Twitter*
|
||||
- *WhatsApp*
|
||||
- *WeChat*
|
||||
|
||||
## Credits <a id="credits"></a>
|
||||
|
||||
@ -95,6 +96,7 @@ _ = notifier.Send(
|
||||
- Slack support: [slack-go/slack](https://github.com/slack-go/slack)
|
||||
- Telegram support: [go-telegram-bot-api/telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api)
|
||||
- Twitter: [dghubble/go-twitter](https://github.com/dghubble/go-twitter)
|
||||
- WeChat: [silenceper/wechat](https://github.com/silenceper/wechat)
|
||||
- WhatsApp: [Rhymen/go-whatsapp](https://github.com/Rhymen/go-whatsapp)
|
||||
|
||||
## Author <a id="author"></a>
|
||||
|
1
go.mod
1
go.mod
@ -30,6 +30,7 @@ require (
|
||||
github.com/plivo/plivo-go v5.5.1+incompatible
|
||||
github.com/sendgrid/rest v2.6.3+incompatible // indirect
|
||||
github.com/sendgrid/sendgrid-go v3.8.0+incompatible
|
||||
github.com/silenceper/wechat/v2 v2.0.5
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/slack-go/slack v0.8.1
|
||||
|
22
go.sum
22
go.sum
@ -35,6 +35,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.1.2/go.mod h1:zu7rotIY9P4Aoc6ytqLP9j
|
||||
github.com/aws/smithy-go v1.2.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/aws/smithy-go v1.3.1 h1:xJFO4pK0y9J8fCl34uGsSJX5KNnGbdARDlA5BPhXnwE=
|
||||
github.com/aws/smithy-go v1.3.1/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
|
||||
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@ -64,6 +66,8 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
@ -85,6 +89,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/gomodule/redigo v1.8.1 h1:Abmo0bI7Xf0IhdIPc7HZQzZcShdnmxeoVuDDtIQp8N8=
|
||||
github.com/gomodule/redigo v1.8.1/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@ -103,6 +109,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
@ -112,6 +120,7 @@ github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
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=
|
||||
@ -132,6 +141,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
@ -157,6 +168,9 @@ github.com/sendgrid/rest v2.6.3+incompatible h1:h/uruXAzKxVyDDIQX/MkQI73p/gsdpEn
|
||||
github.com/sendgrid/rest v2.6.3+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||
github.com/sendgrid/sendgrid-go v3.8.0+incompatible h1:7yoUFMwT+jDI2ArBpC6zvtuQj1RUyYfCDl7zZea3XV4=
|
||||
github.com/sendgrid/sendgrid-go v3.8.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
github.com/silenceper/wechat/v2 v2.0.5 h1:yBOeTMiMAzCAvmhR14ADMlrRrR3oOqmoz7I/bus7bTw=
|
||||
github.com/silenceper/wechat/v2 v2.0.5/go.mod h1:hksYXWXGl7/E6TQojFNgxv8ouTF9CPPjfvWWJouJJGs=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
|
||||
@ -166,11 +180,14 @@ github.com/slack-go/slack v0.8.1 h1:NqGXuzni8Is3EJWmsuMuBiCCPbWOlBgTKPvdlwS3Huk=
|
||||
github.com/slack-go/slack v0.8.1/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
|
||||
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
|
||||
github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -179,6 +196,7 @@ github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/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-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
@ -209,6 +227,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -257,8 +276,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
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.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
84
service/wechat/README.md
Normal file
84
service/wechat/README.md
Normal file
@ -0,0 +1,84 @@
|
||||
# WeChat
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To use the service you need to apply for a [WeChat Official Account](https://mp.weixin.qq.com).
|
||||
Application approval is a manual process and might take a while. In the meantime, you
|
||||
can test your code using the [sandbox](https://developers.weixin.qq.com/sandbox).
|
||||
|
||||
You need the following configuration information to sent out text messages with an Official Account:
|
||||
|
||||
1. AppID
|
||||
2. AppSecret
|
||||
3. Token
|
||||
4. EncodingAESKey
|
||||
5. Verification URL
|
||||
|
||||
The `AppID` and `AppSecret` are provided to you by WeChat. The `Token`, `EncodingAESKey` and
|
||||
the Verifications URL, you set yourself and are needed by the authentication method. More on
|
||||
this [here](https://developers.weixin.qq.com/doc/offiaccount/en/Basic_Information/Access_Overview.html).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Until your application is approved, sign in to the sandbox to get an `AppID`, an `AppSecret` and
|
||||
set the `Token` and the Verification URL. Typically, you need a service like [ngrok](https://ngrok.com/)
|
||||
for the latter. You don't need to/cannot set the `EncodingAESKey`, because it's not required in the
|
||||
sandbox environment:
|
||||
|
||||
![wechat_sandbox_1.png](img/wechat_sandbox_1.png)
|
||||
|
||||
You also need a user subscribed to your Official Account. You can use your own:
|
||||
|
||||
![wechat_sandbox_2.png](img/wechat_sandbox_2.png)
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/silenceper/wechat/v2/cache"
|
||||
"log"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"github.com/nikoksr/notify"
|
||||
"github.com/nikoksr/notify/service/wechat"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
wechatSvc := wechat.New(&wechat.Config{
|
||||
AppID: "abcdefghi",
|
||||
AppSecret: "jklmnopqr",
|
||||
Token: "mytoken",
|
||||
EncodingAESKey: "IGNORED-IN-SANDBOX",
|
||||
Cache: cache.NewMemory(),
|
||||
})
|
||||
|
||||
// do this only once, or when settings are updated
|
||||
devMode := true
|
||||
wechatSvc.WaitForOneOffVerification(":7999", devMode, func(r *http.Request, verified bool) {
|
||||
if !verified {
|
||||
fmt.Println("unknown or failed verification call")
|
||||
} else {
|
||||
fmt.Println("verification call done")
|
||||
}
|
||||
})
|
||||
|
||||
wechatSvc.AddReceivers("some-user-openid")
|
||||
|
||||
notifier := notify.New()
|
||||
notifier.UseServices(wechatSvc)
|
||||
|
||||
err := notifier.Send(context.Background(), "subject", "message")
|
||||
if err != nil {
|
||||
log.Fatalf("notifier.Send() failed: %s", err.Error())
|
||||
}
|
||||
|
||||
log.Println("notification sent")
|
||||
}
|
||||
```
|
||||
|
||||
![wechat_usage.png](img/wechat_usage.png)
|
||||
|
BIN
service/wechat/img/wechat_sandbox_1.png
Normal file
BIN
service/wechat/img/wechat_sandbox_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
BIN
service/wechat/img/wechat_sandbox_2.png
Normal file
BIN
service/wechat/img/wechat_sandbox_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
BIN
service/wechat/img/wechat_usage.png
Normal file
BIN
service/wechat/img/wechat_usage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
27
service/wechat/mock_wechatMessageManager.go
Normal file
27
service/wechat/mock_wechatMessageManager.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Code generated by mockery 2.7.4. DO NOT EDIT.
|
||||
|
||||
package wechat
|
||||
|
||||
import (
|
||||
message "github.com/silenceper/wechat/v2/officialaccount/message"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// mockWechatMessageManager is an autogenerated mock type for the wechatMessageManager type
|
||||
type mockWechatMessageManager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Send provides a mock function with given fields: msg
|
||||
func (_m *mockWechatMessageManager) Send(msg *message.CustomerMessage) error {
|
||||
ret := _m.Called(msg)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*message.CustomerMessage) error); ok {
|
||||
r0 = rf(msg)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
145
service/wechat/wechat.go
Normal file
145
service/wechat/wechat.go
Normal file
@ -0,0 +1,145 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/silenceper/wechat/v2"
|
||||
"github.com/silenceper/wechat/v2/cache"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/config"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/message"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
type verificationCallbackFunc func(r *http.Request, verified bool)
|
||||
|
||||
// Config is the Service configuration.
|
||||
type Config struct {
|
||||
AppID string
|
||||
AppSecret string
|
||||
Token string
|
||||
EncodingAESKey string
|
||||
Cache cache.Cache
|
||||
}
|
||||
|
||||
// wechatMessageManager abstracts go-wechat's message.Manager for writing unit tests
|
||||
type wechatMessageManager interface {
|
||||
Send(msg *message.CustomerMessage) error
|
||||
}
|
||||
|
||||
// Service encapsulates the WeChat client along with internal state for storing users.
|
||||
type Service struct {
|
||||
config *Config
|
||||
messageManager wechatMessageManager
|
||||
userIDs []string
|
||||
}
|
||||
|
||||
// New returns a new instance of a WeChat notification service.
|
||||
func New(cfg *Config) *Service {
|
||||
wc := wechat.NewWechat()
|
||||
wcCfg := &config.Config{
|
||||
AppID: cfg.AppID,
|
||||
AppSecret: cfg.AppSecret,
|
||||
Token: cfg.Token,
|
||||
EncodingAESKey: cfg.EncodingAESKey,
|
||||
Cache: cfg.Cache,
|
||||
}
|
||||
|
||||
oa := wc.GetOfficialAccount(wcCfg)
|
||||
|
||||
return &Service{
|
||||
config: cfg,
|
||||
messageManager: oa.GetCustomerMessageManager(),
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForOneOffVerification waits for the verification call from the WeChat backend.
|
||||
//
|
||||
// Should be running when (re-)applying settings in wechat configuration.
|
||||
//
|
||||
// Set devMode to true when using the sandbox.
|
||||
//
|
||||
// See https://developers.weixin.qq.com/doc/offiaccount/en/Basic_Information/Access_Overview.html
|
||||
func (s *Service) WaitForOneOffVerification(serverURL string, devMode bool, callback verificationCallbackFunc) error {
|
||||
srv := &http.Server{Addr: serverURL}
|
||||
verificationDone := &sync.WaitGroup{}
|
||||
verificationDone.Add(1)
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
|
||||
echoStr := query.Get("echostr")
|
||||
if devMode {
|
||||
if callback != nil {
|
||||
callback(r, true)
|
||||
}
|
||||
_, _ = w.Write([]byte(echoStr))
|
||||
// verification done; dev mode
|
||||
verificationDone.Done()
|
||||
return
|
||||
}
|
||||
|
||||
// perform signature check
|
||||
timestamp := query.Get("timestamp")
|
||||
nonce := query.Get("nonce")
|
||||
suppliedSignature := query.Get("signature")
|
||||
computedSignature := util.Signature(s.config.Token, timestamp, nonce)
|
||||
if suppliedSignature == computedSignature {
|
||||
if callback != nil {
|
||||
callback(r, true)
|
||||
}
|
||||
_, _ = w.Write([]byte(echoStr))
|
||||
// verification done; prod mode
|
||||
verificationDone.Done()
|
||||
return
|
||||
}
|
||||
|
||||
// verification not done (keep waiting)
|
||||
if callback != nil {
|
||||
callback(r, false)
|
||||
}
|
||||
})
|
||||
|
||||
var err error
|
||||
go func() {
|
||||
if innerErr := srv.ListenAndServe(); innerErr != http.ErrServerClosed {
|
||||
err = errors.Wrapf(innerErr, "failed to start verification listener '%s'", serverURL)
|
||||
}
|
||||
}()
|
||||
|
||||
// wait until verification is done and shutdown the server
|
||||
verificationDone.Wait()
|
||||
|
||||
if innerErr := srv.Shutdown(context.TODO()); innerErr != nil {
|
||||
err = errors.Wrap(innerErr, "failed to shutdown verification listerer")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// AddReceivers takes user ids and adds them to the internal users list. The Send method will send
|
||||
// a given message to all those users.
|
||||
func (s *Service) AddReceivers(userIDs ...string) {
|
||||
s.userIDs = append(s.userIDs, userIDs...)
|
||||
}
|
||||
|
||||
// Send takes a message subject and a message content and sends them to all previously set users.
|
||||
func (s *Service) Send(ctx context.Context, subject, content string) error {
|
||||
for _, userID := range s.userIDs {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
text := fmt.Sprintf("%s\n%s", subject, content)
|
||||
err := s.messageManager.Send(message.NewCustomerTextMessage(userID, text))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to send message to WeChat user '%s'", userID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
53
service/wechat/wechat_test.go
Normal file
53
service/wechat/wechat_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/silenceper/wechat/v2/officialaccount/message"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddReceivers(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
svc := &Service{
|
||||
userIDs: []string{},
|
||||
}
|
||||
userIDs := []string{"User1ID", "User2ID", "User3ID"}
|
||||
svc.AddReceivers(userIDs...)
|
||||
|
||||
assert.Equal(svc.userIDs, userIDs)
|
||||
}
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
svc := &Service{
|
||||
userIDs: []string{},
|
||||
}
|
||||
|
||||
// test wechat message manager returning error
|
||||
mockMsgManager := new(mockWechatMessageManager)
|
||||
mockMsgManager.On("Send", message.NewCustomerTextMessage("UserID1", "subject\nmessage")).
|
||||
Return(errors.New("some error"))
|
||||
svc.messageManager = mockMsgManager
|
||||
svc.AddReceivers("UserID1")
|
||||
ctx := context.Background()
|
||||
err := svc.Send(ctx, "subject", "message")
|
||||
assert.NotNil(err)
|
||||
mockMsgManager.AssertExpectations(t)
|
||||
|
||||
// test success and multiple receivers
|
||||
mockMsgManager = new(mockWechatMessageManager)
|
||||
mockMsgManager.On("Send", message.NewCustomerTextMessage("UserID1", "subject\nmessage")).
|
||||
Return(nil)
|
||||
mockMsgManager.On("Send", message.NewCustomerTextMessage("UserID2", "subject\nmessage")).
|
||||
Return(nil)
|
||||
svc.messageManager = mockMsgManager
|
||||
svc.AddReceivers("UserID1", "UserID2")
|
||||
err = svc.Send(ctx, "subject", "message")
|
||||
assert.Nil(err)
|
||||
mockMsgManager.AssertExpectations(t)
|
||||
}
|
Loading…
Reference in New Issue
Block a user