Skip to content

Commit

Permalink
feat(service): Added Zulip service support, closes #457
Browse files Browse the repository at this point in the history
  • Loading branch information
disc committed Oct 3, 2023
1 parent 999c6ff commit 6c8387b
Show file tree
Hide file tree
Showing 8 changed files with 476 additions and 0 deletions.
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.143.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

0 comments on commit 6c8387b

Please sign in to comment.