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

Add Rocket.Chat hive #261

Merged
merged 2 commits into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Binary file added assets/bees/rocketchatbee.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 63 additions & 0 deletions bees/rocketchatbee/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Rocket.chat bee

The [Rocket.Chat](https://rocket.chat/) bee can send messages into a Rocket.Chat channel.

## Configuration

### Options

```json
"Bees":[
{
"Name":"rocketchatmsg",
"Class":"rocketchatbee",
"Description":"my rocket.chat bee",
"Options":[
{
"Name":"url",
"Value":"http://localhost:3000"
},
{
"Name":"user_id",
"Value":"YOUR_USER_ID"
},
{
"Name":"auth_token",
"Value":"YOUR_AUTH_TOKEN"
}
]
}
]
```

See https://rocket.chat/docs/developer-guides/rest-api/personal-access-tokens/ for reference on how to create a `user_id` and `auth_token`.

### Actions

**send**: send a message to a Rocket.Chat channel. This needs the name of the `channel`, and the `text` to send. If you specify an `alias`, messages posted appear under that username.

```json
"Elements":[
{
"Action":{
"Bee":"rocketchatmsg",
"Name":"send",
"Options":[
{
"Name":"channel",
"Value":"info"
},
{
"Name":"text",
"Value":"This is the latest info!"
},
{
"Name":"alias",
"Value":"Informer"
}
]
]
}
}
]
```
190 changes: 190 additions & 0 deletions bees/rocketchatbee/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* Copyright (C) 2016 Sergio Rubio
* 2017 Christian Muehlhaeuser
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to add yourself here and a couple lines below under Authors:.

*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Sergio Rubio <rubiojr@frameos.org>
* Christian Muehlhaeuser <muesli@gmail.com>
*/

// Package rocketchatbee is a Bee that can connect to Rocketchat.
package rocketchatbee

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)

type Client struct {
httpClient *http.Client
url string
userID string
authToken string
}

// Message defines a chat message
type Message struct {
Text string `json:"msg"`
ChannelID string `json:"rid"`
Alias string `json:"alias,omitempty"`
}

// MessageContainer is sent to Rocket.Chat to create a chat message
type MessageContainer struct {
Message Message `json:"message"`
}

// Room is a Channel in Rocket.Chat
type Room struct {
ID string `json:"_id"`
}

// RoomResponseContainer is used to receive the ID of a room/channel
type RoomResponseContainer struct {
Room Room `json:"room"`
}

// ErrorResponse
type ErrorResponse struct {
Error string `json:"error"`
}

func NewClient(url, userID, authToken string) *Client {
return &Client{
&http.Client{
Timeout: 30 * time.Second,
},
url,
userID,
authToken,
}
}

func (c *Client) newRequest(method, path string, body interface{}) (*http.Request, error) {
url := fmt.Sprintf("%s/%s", c.url, path)

var buf io.ReadWriter
if body != nil {
buf = new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}

req, err := http.NewRequest(method, url, buf)
if err != nil {
return nil, err
}

if body != nil {
req.Header.Set("Content-Type", "application/json")
}

req.Header.Set("Accept", "application/json")
req.Header.Set("X-User-Id", c.userID)
req.Header.Set("X-Auth-Token", c.authToken)

return req, nil
}

func (c *Client) do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode > 399 {
var errorResponse ErrorResponse
err = json.NewDecoder(resp.Body).Decode(&errorResponse)
if err != nil {
return nil, fmt.Errorf("request to '%s' failed with http %d: %s", resp.Request.URL, resp.StatusCode, err)
}
return nil, fmt.Errorf("request to '%s' failed with http code %d: %s", resp.Request.URL, resp.StatusCode, errorResponse.Error)
}

// nil for empty responses
if v != nil {
err = json.NewDecoder(resp.Body).Decode(v)
}
return resp, err
}

func (c *Client) SendMessage(channel, text, alias string) error {
var (
channelID string
)

channelID, err := c.GetChannelID(channel)
if err != nil {
return err
}

msg := &MessageContainer{
Message{
Text: text,
ChannelID: channelID,
Alias: alias,
},
}

req, err := c.newRequest("POST", "/api/v1/chat.sendMessage", msg)
if err != nil {
return err
}

// errors are handeled in do
_, err = c.do(req, nil)
return err
}

// getChannelID returns the ID of a channel
func (c *Client) GetChannelID(name string) (string, error) {

req, err := c.newRequest("GET", "/api/v1/rooms.info", nil)

// add query parameter roomName
q := req.URL.Query()
q.Add("roomName", name)
req.URL.RawQuery = q.Encode()

rc := &RoomResponseContainer{}
_, err = c.do(req, rc)
if err != nil {
return "", err
}
return rc.Room.ID, nil
}

func (c *Client) TestConnection() error {

req, err := c.newRequest("GET", "/api/v1/me", nil)
if err != nil {
return err
}

_, err = c.do(req, nil)
if err != nil {
return err
}

return nil
}
87 changes: 87 additions & 0 deletions bees/rocketchatbee/rocketchatbee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2016 Sergio Rubio
* 2017 Christian Muehlhaeuser
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Sergio Rubio <rubiojr@frameos.org>
* Christian Muehlhaeuser <muesli@gmail.com>
*/

// Package rocketchatbee is a Bee that can connect to Rocketchat.
package rocketchatbee

import (
"github.com/muesli/beehive/bees"
)

// RocketchatBee is a Bee that can connect to Rocketchat.
type RocketchatBee struct {
bees.Bee

client *Client
}

// Action triggers the action passed to it.
func (mod *RocketchatBee) Action(action bees.Action) []bees.Placeholder {
outs := []bees.Placeholder{}

switch action.Name {
case "send":
var (
text string
channel string
alias string
)

action.Options.Bind("text", &text)
action.Options.Bind("channel", &channel)
action.Options.Bind("alias", &alias)

err := mod.client.SendMessage(channel, text, alias)
if err != nil {
mod.LogErrorf("Failed to send message: %s", err)
return outs
}

default:
mod.LogErrorf("Unknown action triggered in " + mod.Name() + ": " + action.Name)
}

return outs
}

// ReloadOptions parses the config options and initializes the Bee.
func (mod *RocketchatBee) ReloadOptions(options bees.BeeOptions) {
var (
url string
userID string
authToken string
)

mod.SetOptions(options)

options.Bind("url", &url)
options.Bind("user_id", &userID)
options.Bind("auth_token", &authToken)

mod.client = NewClient(url, userID, authToken)

err := mod.client.TestConnection()
if err != nil {
mod.LogErrorf("Connection to Rocket.Chat failed: %s", err)
}

}
Loading