Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(service): Zulip service support #706

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Yes, please! Contributions of all kinds are very welcome! Feel free to check our
| [WeChat](https://www.wechat.com) | [service/wechat](service/wechat) | [silenceper/wechat](https://github.com/silenceper/wechat) | :heavy_check_mark: |
| [Webpush Notification](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) | [service/webpush](service/webpush) | [SherClockHolmes/webpush-go](https://github.com/SherClockHolmes/webpush-go/) | :heavy_check_mark: |
| [WhatsApp](https://www.whatsapp.com) | [service/whatsapp](service/whatsapp) | [Rhymen/go-whatsapp](https://github.com/Rhymen/go-whatsapp) | :x: |
| [Zulip](https://zulip.com/) | [service/zulip](service/zulip) | [ifo/gozulipbot](https://github.com/ifo/gozulipbot) | :heavy_check_mark: |

## Special Thanks <a id="special_thanks"></a>

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ require (
require github.com/golang-jwt/jwt v3.2.2+incompatible // indirect

require (
github.com/ifo/gozulipbot v0.0.1
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/vartanbeno/go-reddit/v2 v2.0.1
google.golang.org/api v0.145.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
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/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ifo/gozulipbot v0.0.1 h1:hYcUViKBe1ZIXk+WRNOe2dEFGi6H8G1cr25HsEnN9Xw=
github.com/ifo/gozulipbot v0.0.1/go.mod h1:KBDdzKbjflzh+LBaYauJmuDCwwXfFLI6j3eSENlScE0=
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=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
Expand Down
24 changes: 24 additions & 0 deletions service/zulip/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package zulip

// Receiver encapsulates a receiver credentials for a direct or stream message.
type Receiver struct {
email string
stream string
topic string
}

// Direct specifies a Zulip Direct message
func Direct(email string) *Receiver {
return &Receiver{email: email}
}

// Stream specifies a Zulip Stream message
func Stream(stream, topic string) *Receiver {
return &Receiver{stream: stream, topic: topic}
}

type ErrorResponse struct {
Code string `json:"code"`
Message string `json:"msg"`
Result string `json:"result"`
}
56 changes: 56 additions & 0 deletions service/zulip/mock_zulip_client.go

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

62 changes: 62 additions & 0 deletions service/zulip/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Zulip Usage

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

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

## Steps for creating Zulip Bot

These are general and very high level instructions

1. Create a new Zulip bot (https://zulip.com/help/add-a-bot-or-integration)
2. Copy your *Organization URL* from the browser address bar. You need to copy only subdomain `your-org` from the full url `your-org.zulipchat.com` without the hostname `.zulipchat.com`.
3. Copy your *Bot Email* and *API Key* for usage below
4. Copy the *Stream name* of the stream if you want to post a message to stream or just copy an email address of the receiver.
5. Now you should be good to use the code below

## Sample Code

```go
package main

import (
"context"
"fmt"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/zulip"
)

func main() {

notifier := notify.New()

// Provide your Zulip Bot credentials
zulipService := zulip.New(
"your-org",
"ZULIP_API_KEY",
"[email protected]",
)

// Passing a Zulip receivers as a receiver for our messages.
// Where to send our messages.
// It can be direct or stream message
zulipService.AddReceivers(zulip.Direct("[email protected]"))
zulipService.AddReceivers(zulip.Stream("alerts", "critical"))

// Tell our notifier to use the Zulip service. You can repeat the above process
// for as many services as you like and just tell the notifier to use them.
notifier.UseServices(zulipService)

// Send a message
err := notifier.Send(
context.Background(),
"Hello from notify :wave:\n",
"Message written in Go!",
)

if err != nil {
fmt.Println(err)
}

}
```
93 changes: 93 additions & 0 deletions service/zulip/zulip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package zulip

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"

gzb "github.com/ifo/gozulipbot"
"github.com/pkg/errors"
)

//go:generate mockery --name=zulipClient --output=. --case=underscore --inpackage
type zulipClient interface {
Message(gzb.Message) (*http.Response, error)
}

// Compile-time check to ensure that zulip message client implements the zulipClient interface.
var _ zulipClient = new(gzb.Bot)

// Zulip struct holds necessary data to communicate with the Zulip API.
type Zulip struct {
client zulipClient
receivers []*Receiver
}

func New(domain, apiKey, botEmail string) *Zulip {
client := &gzb.Bot{
APIURL: fmt.Sprintf("https://%s.zulipchat.com/api/v1/", domain),
APIKey: apiKey,
Email: botEmail,
}

client.Init()

zulip := &Zulip{
client: client,
receivers: make([]*Receiver, 0),
}

return zulip
}

func (z *Zulip) AddReceivers(receivers ...*Receiver) {
z.receivers = append(z.receivers, receivers...)
}

func (z *Zulip) Send(ctx context.Context, subject, message string) error {
fullMessage := subject + "\n" + message // Treating subject as message title

for _, receiver := range z.receivers {
select {
case <-ctx.Done():
return ctx.Err()
default:
emails := make([]string, 0)
if receiver.email != "" {
emails = append(emails, receiver.email)
}

msg := gzb.Message{
Content: fullMessage,
Emails: emails,
Stream: receiver.stream,
Topic: receiver.topic,
}

resp, err := z.client.Message(msg)
if err != nil {
return errors.Wrapf(err, "failed to send message to Zulip receiver")
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)

switch resp.StatusCode {
case http.StatusBadRequest:
var errorResp ErrorResponse
_ = json.Unmarshal(body, &errorResp)

return errors.Errorf("failed to send message to Zulip receiver: %s", errorResp.Message)

case http.StatusOK:
break

default:
return errors.Errorf("failed to send message to Zulip receiver: %s", body)
}
}
}

return nil
}
Loading