mirror of
https://github.com/nikoksr/notify.git
synced 2026-06-19 22:59:02 +02:00
Merge pull request #300 from immanuelhume/feat/lark
This commit is contained in:
@@ -86,6 +86,7 @@ Yes, please! Contributions of all kinds are very welcome! Feel free to check our
|
|||||||
| [Discord](https://discord.com) | [service/discord](service/discord) | [bwmarrin/discordgo](https://github.com/bwmarrin/discordgo) |
|
| [Discord](https://discord.com) | [service/discord](service/discord) | [bwmarrin/discordgo](https://github.com/bwmarrin/discordgo) |
|
||||||
| [Email](https://wikipedia.org/wiki/Email) | [service/mail](service/mail) | [jordan-wright/email](https://github.com/jordan-wright/email) |
|
| [Email](https://wikipedia.org/wiki/Email) | [service/mail](service/mail) | [jordan-wright/email](https://github.com/jordan-wright/email) |
|
||||||
| [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [service/fcm](service/fcm) | [appleboy/go-fcm](https://github.com/appleboy/go-fcm) |
|
| [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [service/fcm](service/fcm) | [appleboy/go-fcm](https://github.com/appleboy/go-fcm) |
|
||||||
|
| [Lark](https://www.larksuite.com/) | [service/lark](service/lark) | [go-lark/lark](https://github.com/go-lark/lark) |
|
||||||
| [Line](https://line.me) | [service/line](service/line) | [line/line-bot-sdk-go](https://github.com/line/line-bot-sdk-go) |
|
| [Line](https://line.me) | [service/line](service/line) | [line/line-bot-sdk-go](https://github.com/line/line-bot-sdk-go) |
|
||||||
| [Line Notify](https://notify-bot.line.me) | [service/line](service/line) | [utahta/go-linenotify](https://github.com/utahta/go-linenotify) |
|
| [Line Notify](https://notify-bot.line.me) | [service/line](service/line) | [utahta/go-linenotify](https://github.com/utahta/go-linenotify) |
|
||||||
| [Mailgun](https://www.mailgun.com) | [service/mailgun](service/mailgun) | [mailgun/mailgun-go](https://github.com/mailgun/mailgun-go) |
|
| [Mailgun](https://www.mailgun.com) | [service/mailgun](service/mailgun) | [mailgun/mailgun-go](https://github.com/mailgun/mailgun-go) |
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/appleboy/go-fcm v0.1.5
|
github.com/appleboy/go-fcm v0.1.5
|
||||||
|
github.com/go-lark/lark v1.7.2
|
||||||
github.com/google/go-cmp v0.5.8
|
github.com/google/go-cmp v0.5.8
|
||||||
github.com/kevinburke/twilio-go v0.0.0-20220615032439-b0fe9b151b0e
|
github.com/kevinburke/twilio-go v0.0.0-20220615032439-b0fe9b151b0e
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/go-lark/lark v1.7.2 h1:F2LlwbRrZVuHPs8lz1D7owDevUndPy88Hbw6ZXaho/A=
|
||||||
|
github.com/go-lark/lark v1.7.2/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
|
||||||
github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc h1:jZY+lpZB92nvBo2f31oPC/ivGll6NcsnEOORm8Fkr4M=
|
github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc h1:jZY+lpZB92nvBo2f31oPC/ivGll6NcsnEOORm8Fkr4M=
|
||||||
github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ=
|
github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
@@ -136,6 +138,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
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/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
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/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
@@ -334,5 +338,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# Lark
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Depending on your requirements, you'll need either a custom app or a Lark group
|
||||||
|
chat webhook. The latter is easier to set up, but can only send messages to the
|
||||||
|
group it is in. You may refer to the doc
|
||||||
|
[here](https://open.larksuite.com/document/uAjLw4CM/ukTMukTMuhttps://open.larksuite.com/document/home/develop-a-bot-in-5-minutes/create-an-appkTM/bot-v3/use-custom-bots-in-a-group)
|
||||||
|
to set up a webhook bot, and the doc
|
||||||
|
[here](https://open.larksuite.com/document/home/develop-a-bot-in-5-minutes/create-an-app)
|
||||||
|
to set up a custom app.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Webhook
|
||||||
|
|
||||||
|
For webhook bots, we only need the webhook URL, which might look something like
|
||||||
|
`https://open.feishu.cn/open-apis/bot/v2/hook/xxx`. Note that there is no
|
||||||
|
method to configure receivers, because the webhook bot can only send messages
|
||||||
|
to the group in which it was created.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/nikoksr/notify"
|
||||||
|
"github.com/nikoksr/notify/service/lark"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace this with your own webhook URL.
|
||||||
|
const webHookURL = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
larkWebhookSvc := lark.NewWebhookService(webHookURL)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(larkWebhookSvc)
|
||||||
|
|
||||||
|
if err := notifier.Send(context.Background(), "subject", "message"); err != nil {
|
||||||
|
log.Fatalf("notifier.Send() failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("notification sent")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom App
|
||||||
|
|
||||||
|
For custom apps, we need to pass in the App ID and App Secret when creating a
|
||||||
|
new notification service. When adding receivers, the type of the receiver ID
|
||||||
|
must be specified, as shown in the example below. You may refer to the section
|
||||||
|
entitled "Query parameters" in the doc
|
||||||
|
[here](https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create)
|
||||||
|
for more information about the different ID types.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/nikoksr/notify"
|
||||||
|
"github.com/nikoksr/notify/service/lark"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace these with the credentials from your custom app.
|
||||||
|
const (
|
||||||
|
appId = "xxx"
|
||||||
|
appSecret = "xxx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
larkCustomAppService := lark.NewCustomAppService(appId, appSecret)
|
||||||
|
|
||||||
|
// Lark implements five types of receiver IDs. You'll need to specify the
|
||||||
|
// type using the respective helper functions when adding them as receivers.
|
||||||
|
larkCustomAppService.AddReceivers(
|
||||||
|
lark.OpenID("xxx"),
|
||||||
|
lark.UserID("xxx"),
|
||||||
|
lark.UnionID("xxx"),
|
||||||
|
lark.Email("xyz@example.com"),
|
||||||
|
lark.ChatID("xxx"),
|
||||||
|
)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(larkCustomAppService)
|
||||||
|
|
||||||
|
if err := notifier.Send(context.Background(), "subject", "message"); err != nil {
|
||||||
|
log.Fatalf("notifier.Send() failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("notification sent")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package lark
|
||||||
|
|
||||||
|
// sender is an interface for sending a message to an already defined receiver.
|
||||||
|
type sender interface {
|
||||||
|
Send(subject, message string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// sender is an interface for sending a message to a specific receiver ID.
|
||||||
|
type sendToer interface {
|
||||||
|
SendTo(subject, message, id, idType string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// receiverID encapsulates a receiver ID and its type in Lark.
|
||||||
|
type receiverID struct {
|
||||||
|
id string
|
||||||
|
typ receiverIDType
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenID specifies an ID as a Lark Open ID.
|
||||||
|
func OpenID(s string) *receiverID {
|
||||||
|
return &receiverID{s, openID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserID specifies an ID as a Lark User ID.
|
||||||
|
func UserID(s string) *receiverID {
|
||||||
|
return &receiverID{s, userID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnionID specifies an ID as a Lark Union ID.
|
||||||
|
func UnionID(s string) *receiverID {
|
||||||
|
return &receiverID{s, unionID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email specifies a receiver ID as an email.
|
||||||
|
func Email(s string) *receiverID {
|
||||||
|
return &receiverID{s, email}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatID specifies an ID as a Lark Chat ID.
|
||||||
|
func ChatID(s string) *receiverID {
|
||||||
|
return &receiverID{s, chatID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// receiverIDType represents the different ID types implemented by Lark. This
|
||||||
|
// information is required when sending a message. More information about the
|
||||||
|
// different ID types can be found in the "Query parameters" section of
|
||||||
|
// the https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create,
|
||||||
|
// or on
|
||||||
|
// https://open.larksuite.com/document/home/user-identity-introduction/introduction.
|
||||||
|
type receiverIDType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
openID receiverIDType = "open_id"
|
||||||
|
userID receiverIDType = "user_id"
|
||||||
|
unionID receiverIDType = "union_id"
|
||||||
|
email receiverIDType = "email"
|
||||||
|
chatID receiverIDType = "chat_id"
|
||||||
|
)
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package lark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-lark/lark"
|
||||||
|
|
||||||
|
"github.com/nikoksr/notify"
|
||||||
|
)
|
||||||
|
|
||||||
|
type customAppService struct {
|
||||||
|
receiveIDs []*receiverID
|
||||||
|
cli sendToer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile time check that larkCustomAppService implements notify.Notifer.
|
||||||
|
var _ notify.Notifier = &customAppService{}
|
||||||
|
|
||||||
|
// NewCustomAppService returns a new instance of a Lark notify service using a
|
||||||
|
// Lark custom app.
|
||||||
|
func NewCustomAppService(appID, appSecret string) *customAppService {
|
||||||
|
bot := lark.NewChatBot(appID, appSecret)
|
||||||
|
|
||||||
|
// We need to set the bot to use Lark's open.larksuite.com domain instead of
|
||||||
|
// the default open.feishu.cn domain.
|
||||||
|
bot.SetDomain(lark.DomainLark)
|
||||||
|
|
||||||
|
// Let the bot use a HTTP client with a longer timeout than the default 5
|
||||||
|
// seconds.
|
||||||
|
bot.SetClient(&http.Client{
|
||||||
|
Timeout: 8 * time.Second,
|
||||||
|
})
|
||||||
|
|
||||||
|
_ = bot.StartHeartbeat()
|
||||||
|
|
||||||
|
return &customAppService{
|
||||||
|
receiveIDs: make([]*receiverID, 0),
|
||||||
|
cli: &larkClientGoLarkChatBot{
|
||||||
|
bot: bot,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReceivers adds recipients to future notifications. There are five different
|
||||||
|
// types of receiver IDs available in Lark and they must be specified here. For
|
||||||
|
// example:
|
||||||
|
//
|
||||||
|
// larkService.AddReceivers(
|
||||||
|
// lark.OpenID("ou_c99c5f35d542efc7ee492afe11af19ef"),
|
||||||
|
// lark.UserID("8335aga2"),
|
||||||
|
// lark.UnionID("on_cad4860e7af114fb4ff6c5d496d1dd76"),
|
||||||
|
// lark.Email("xyz@example.com"),
|
||||||
|
// lark.ChatID("oc_a0553eda9014c201e6969b478895c230"),
|
||||||
|
// )
|
||||||
|
func (c *customAppService) AddReceivers(ids ...*receiverID) {
|
||||||
|
c.receiveIDs = append(c.receiveIDs, ids...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send takes a message subject and a message body and sends them to all
|
||||||
|
// previously registered recipient IDs.
|
||||||
|
func (c *customAppService) Send(ctx context.Context, subject, message string) error {
|
||||||
|
for _, id := range c.receiveIDs {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := c.cli.SendTo(subject, message, id.id, string(id.typ)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// larkClientGoLarkChatBot is a wrapper around go-lark/lark's Bot, to be used
|
||||||
|
// for sending messages with custom apps.
|
||||||
|
type larkClientGoLarkChatBot struct {
|
||||||
|
bot *lark.Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTo implements the sendToer interface using a go-lark/lark chat bot.
|
||||||
|
func (l *larkClientGoLarkChatBot) SendTo(subject, message, receiverID, idType string) error {
|
||||||
|
content := lark.NewPostBuilder().
|
||||||
|
Title(subject).
|
||||||
|
TextTag(message, 1, false).
|
||||||
|
Render()
|
||||||
|
msg := lark.NewMsgBuffer(lark.MsgPost).Post(content)
|
||||||
|
switch receiverIDType(idType) {
|
||||||
|
case openID:
|
||||||
|
msg.BindOpenID(receiverID)
|
||||||
|
case userID:
|
||||||
|
msg.BindUserID(receiverID)
|
||||||
|
case unionID:
|
||||||
|
msg.BindUnionID(receiverID)
|
||||||
|
case email:
|
||||||
|
msg.BindEmail(receiverID)
|
||||||
|
case chatID:
|
||||||
|
msg.BindChatID(receiverID)
|
||||||
|
}
|
||||||
|
res, err := l.bot.PostMessage(msg.Build())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to send message: %w", err)
|
||||||
|
}
|
||||||
|
if res.Code != 0 {
|
||||||
|
return fmt.Errorf("send failed with error code %d, please see https://open.larksuite.com/document/ukTMukTMukTM/ugjM14COyUjL4ITN for details", res.Code)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package lark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddReceivers(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
xs := []*receiverID{
|
||||||
|
OpenID("ou_c99c5f35d542efc7ee492afe11af19ef"),
|
||||||
|
UserID("8335aga2"),
|
||||||
|
}
|
||||||
|
svc := NewCustomAppService("", "")
|
||||||
|
svc.AddReceivers(xs...)
|
||||||
|
|
||||||
|
assert.ElementsMatch(t, svc.receiveIDs, xs)
|
||||||
|
|
||||||
|
// Test if adding more receivers afterwards works.
|
||||||
|
ys := []*receiverID{
|
||||||
|
UnionID("on_cad4860e7af114fb4ff6c5d496d1dd76"),
|
||||||
|
Email("xyz@example.com"),
|
||||||
|
ChatID("oc_a0553eda9014c201e6969b478895c230"),
|
||||||
|
}
|
||||||
|
svc.AddReceivers(ys...)
|
||||||
|
|
||||||
|
assert.ElementsMatch(t, svc.receiveIDs, append(xs, ys...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendCustomApp(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := context.Background()
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
tests := []*receiverID{
|
||||||
|
OpenID("ou_c99c5f35d542efc7ee492afe11af19ef"),
|
||||||
|
UserID("8335aga2"),
|
||||||
|
UnionID("on_cad4860e7af114fb4ff6c5d496d1dd76"),
|
||||||
|
Email("xyz@example.com"),
|
||||||
|
ChatID("oc_a0553eda9014c201e6969b478895c230"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, test for when the sender returns an error.
|
||||||
|
for _, tt := range tests {
|
||||||
|
mockSendToer := NewSendToer(t)
|
||||||
|
mockSendToer.
|
||||||
|
On("SendTo", "subject", "message", tt.id, string(tt.typ)).
|
||||||
|
Return(errors.New(""))
|
||||||
|
|
||||||
|
svc := NewCustomAppService("", "")
|
||||||
|
svc.cli = mockSendToer
|
||||||
|
|
||||||
|
svc.AddReceivers(tt)
|
||||||
|
err := svc.Send(ctx, "subject", "message")
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
mockSendToer.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then test for when the sender does not return an error.
|
||||||
|
for _, tt := range tests {
|
||||||
|
mockSendToer := NewSendToer(t)
|
||||||
|
mockSendToer.
|
||||||
|
On("SendTo", "subject", "message", tt.id, string(tt.typ)).
|
||||||
|
Return(nil)
|
||||||
|
|
||||||
|
svc := NewCustomAppService("", "")
|
||||||
|
svc.cli = mockSendToer
|
||||||
|
|
||||||
|
svc.AddReceivers(tt)
|
||||||
|
err := svc.Send(ctx, "subject", "message")
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
mockSendToer.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Package lark provides message notification integration for Lark. Two kinds of
|
||||||
|
bots on Lark are supported -- webhooks and custom apps. For information on
|
||||||
|
webhook bots, see
|
||||||
|
https://open.larksuite.com/document/uAjLw4CM/ukTMukTMukTM/bot-v3/use-custom-bots-in-a-group,
|
||||||
|
and for info on custom apps, see
|
||||||
|
https://open.larksuite.com/document/home/develop-a-bot-in-5-minutes/create-an-app.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/nikoksr/notify"
|
||||||
|
"github.com/nikoksr/notify/service/lark"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
webhookURL = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
|
||||||
|
appId = "xxx"
|
||||||
|
appSecret = "xxx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Two types of services are available depending on your requirements.
|
||||||
|
larkWebhookService := lark.NewWebhookService(webhookURL)
|
||||||
|
larkCustomAppService := lark.NewCustomAppService(appId, appSecret)
|
||||||
|
|
||||||
|
// Lark implements five types of receiver IDs. You'll need to specify the
|
||||||
|
// type using the respective helper functions when adding them as receivers
|
||||||
|
// for the custom app service.
|
||||||
|
larkCustomAppService.AddReceivers(
|
||||||
|
lark.OpenID("xxx"),
|
||||||
|
lark.UserID("xxx"),
|
||||||
|
lark.UnionID("xxx"),
|
||||||
|
lark.Email("xyz@example.com"),
|
||||||
|
lark.ChatID("xxx"),
|
||||||
|
)
|
||||||
|
|
||||||
|
notifier := notify.New()
|
||||||
|
notifier.UseServices(larkWebhookService, larkCustomAppService)
|
||||||
|
|
||||||
|
if err := notifier.Send(context.Background(), "subject", "message"); err != nil {
|
||||||
|
log.Fatalf("notifier.Send() failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("notification sent")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package lark
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by mockery v2.12.3. DO NOT EDIT.
|
||||||
|
|
||||||
|
package lark
|
||||||
|
|
||||||
|
import "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
// SendToer is an autogenerated mock type for the sendToer type
|
||||||
|
type SendToer struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTo provides a mock function with given fields: subject, message, id, idType
|
||||||
|
func (_m *SendToer) SendTo(subject string, message string, id string, idType string) error {
|
||||||
|
ret := _m.Called(subject, message, id, idType)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string, string, string) error); ok {
|
||||||
|
r0 = rf(subject, message, id, idType)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewSendToerT interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSendToer creates a new instance of SendToer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
func NewSendToer(t NewSendToerT) *SendToer {
|
||||||
|
mock := &SendToer{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by mockery v2.12.3. DO NOT EDIT.
|
||||||
|
|
||||||
|
package lark
|
||||||
|
|
||||||
|
import "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
// Sender is an autogenerated mock type for the sender type
|
||||||
|
type Sender struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send provides a mock function with given fields: subject, message
|
||||||
|
func (_m *Sender) Send(subject string, message string) error {
|
||||||
|
ret := _m.Called(subject, message)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string) error); ok {
|
||||||
|
r0 = rf(subject, message)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewSenderT interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSender creates a new instance of Sender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
func NewSender(t NewSenderT) *Sender {
|
||||||
|
mock := &Sender{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package lark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-lark/lark"
|
||||||
|
|
||||||
|
"github.com/nikoksr/notify"
|
||||||
|
)
|
||||||
|
|
||||||
|
type webhookService struct {
|
||||||
|
cli sender
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile time check that larkCustomAppService implements notify.Notifer.
|
||||||
|
var _ notify.Notifier = &webhookService{}
|
||||||
|
|
||||||
|
// NewWebhookService returns a new instance of a Lark notify service using a
|
||||||
|
// Lark group chat webhook. Note that this service does not take any
|
||||||
|
// notification receivers because it can only push messages to the group chat
|
||||||
|
// it belongs to.
|
||||||
|
func NewWebhookService(webhookURL string) *webhookService {
|
||||||
|
bot := lark.NewNotificationBot(webhookURL)
|
||||||
|
return &webhookService{
|
||||||
|
cli: &larkClientGoLarkNotificationBot{
|
||||||
|
bot: bot,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends the message subject and body to the group chat.
|
||||||
|
func (w *webhookService) Send(ctx context.Context, subject, message string) error {
|
||||||
|
return w.cli.Send(subject, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// larkClientGoLarkNotificationBot is a wrapper around go-lark/lark's Bot, to
|
||||||
|
// be used for notifications via webhooks only.
|
||||||
|
type larkClientGoLarkNotificationBot struct {
|
||||||
|
bot *lark.Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send implements the sender interface using a go-lark/lark notification bot.
|
||||||
|
func (w *larkClientGoLarkNotificationBot) Send(subject, message string) error {
|
||||||
|
content := lark.NewPostBuilder().
|
||||||
|
Title(subject).
|
||||||
|
TextTag(message, 1, false).
|
||||||
|
Render()
|
||||||
|
msg := lark.NewMsgBuffer(lark.MsgPost).Post(content)
|
||||||
|
res, err := w.bot.PostNotificationV2(msg.Build())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to post webhook message: %w", err)
|
||||||
|
}
|
||||||
|
if res.Code != 0 {
|
||||||
|
return fmt.Errorf("send failed with error code %d, please see https://open.larksuite.com/document/ukTMukTMukTM/ugjM14COyUjL4ITN for details", res.Code)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package lark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSendWebhook(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := assert.New(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// First, test for when the sender returns an error.
|
||||||
|
{
|
||||||
|
mockSender := NewSender(t)
|
||||||
|
mockSender.
|
||||||
|
On("Send", "subject", "message").
|
||||||
|
Return(errors.New(""))
|
||||||
|
|
||||||
|
svc := NewWebhookService("")
|
||||||
|
svc.cli = mockSender
|
||||||
|
err := svc.Send(ctx, "subject", "message")
|
||||||
|
assert.NotNil(err)
|
||||||
|
mockSender.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then test for when the sender does not return an error.
|
||||||
|
{
|
||||||
|
mockSender := NewSender(t)
|
||||||
|
mockSender.
|
||||||
|
On("Send", "subject", "message").
|
||||||
|
Return(nil)
|
||||||
|
|
||||||
|
svc := NewWebhookService("")
|
||||||
|
svc.cli = mockSender
|
||||||
|
err := svc.Send(ctx, "subject", "message")
|
||||||
|
assert.Nil(err)
|
||||||
|
mockSender.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user