From 73f01ad8d813505c6309fe568d0abc597cff8790 Mon Sep 17 00:00:00 2001 From: Wim Date: Sat, 18 Feb 2017 23:10:22 +0100 Subject: [PATCH] Add REST API support --- README.md | 3 +- bridge/api/api.go | 91 ++++++++++++++++++++++++++++++++++++++++ bridge/bridge.go | 4 ++ bridge/config/config.go | 17 +++++--- changelog.md | 3 ++ gateway/gateway.go | 6 +++ matterbridge.toml.sample | 29 +++++++++++++ 7 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 bridge/api/api.go diff --git a/README.md b/README.md index dd7b1cbc..add3c639 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # matterbridge ![matterbridge.gif](https://s15.postimg.org/qpjhp6y3f/matterbridge.gif) -Simple bridge between mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat and Hipchat(via xmpp). +Simple bridge between mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat and Hipchat(via xmpp) with REST API. * Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat and Hipchat (via xmpp). Pick and mix. * Supports multiple channels. * Matterbridge can also work with private groups on your mattermost. * Allow for bridging the same bridges, which means you can eg bridge between multiple mattermosts. * The bridge is now a gateway which has support multiple in and out bridges. (and supports multiple gateways). +* REST API to read/post messages to bridges (WIP) Look at [matterbridge.toml.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example. Look at [matterbridge.toml.simple] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.simple) for a simple example. diff --git a/bridge/api/api.go b/bridge/api/api.go new file mode 100644 index 00000000..4870ee6a --- /dev/null +++ b/bridge/api/api.go @@ -0,0 +1,91 @@ +package api + +import ( + "github.com/42wim/matterbridge/bridge/config" + log "github.com/Sirupsen/logrus" + "github.com/labstack/echo" + "github.com/zfjagann/golang-ring" + "net/http" + "sync" +) + +type Api struct { + Config *config.Protocol + Remote chan config.Message + Account string + Messages ring.Ring + sync.RWMutex +} + +type ApiMessage struct { + Text string `json:"text"` + Username string `json:"username"` + Avatar string `json:"avatar"` +} + +var flog *log.Entry +var protocol = "api" + +func init() { + flog = log.WithFields(log.Fields{"module": protocol}) +} + +func New(cfg config.Protocol, account string, c chan config.Message) *Api { + b := &Api{} + e := echo.New() + b.Messages = ring.Ring{} + b.Messages.SetCapacity(cfg.Buffer) + b.Config = &cfg + b.Account = account + b.Remote = c + e.GET("/api/messages", b.handleMessages) + e.POST("/api/message", b.handlePostMessage) + go func() { + flog.Fatal(e.Start(cfg.BindAddress)) + }() + return b +} + +func (b *Api) Connect() error { + return nil +} +func (b *Api) Disconnect() error { + return nil + +} +func (b *Api) JoinChannel(channel string) error { + return nil + +} + +func (b *Api) Send(msg config.Message) error { + b.Lock() + defer b.Unlock() + b.Messages.Enqueue(&msg) + return nil +} + +func (b *Api) handlePostMessage(c echo.Context) error { + message := &ApiMessage{} + if err := c.Bind(message); err != nil { + return err + } + b.Remote <- config.Message{ + Text: message.Text, + Username: message.Username, + Channel: "api", + Avatar: message.Avatar, + Account: b.Account, + } + return c.JSON(http.StatusOK, message) +} + +func (b *Api) handleMessages(c echo.Context) error { + b.Lock() + defer b.Unlock() + for _, msg := range b.Messages.Values() { + c.JSONPretty(http.StatusOK, msg, " ") + } + b.Messages = ring.Ring{} + return nil +} diff --git a/bridge/bridge.go b/bridge/bridge.go index db26c422..012b0efd 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -1,6 +1,7 @@ package bridge import ( + "github.com/42wim/matterbridge/bridge/api" "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/discord" "github.com/42wim/matterbridge/bridge/gitter" @@ -70,6 +71,9 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid case "rocketchat": b.Config = cfg.Rocketchat[name] b.Bridger = brocketchat.New(cfg.Rocketchat[name], bridge.Account, c) + case "api": + b.Config = cfg.Api[name] + b.Bridger = api.New(cfg.Api[name], bridge.Account, c) } return b } diff --git a/bridge/config/config.go b/bridge/config/config.go index 811c97ae..4f6568ab 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -6,6 +6,7 @@ import ( "os" "reflect" "strings" + "time" ) const ( @@ -14,16 +15,19 @@ const ( ) type Message struct { - Text string - Channel string - Username string - Avatar string - Account string - Event string + Text string + Channel string + Username string + Avatar string + Account string + Event string + Protocol string + Timestamp time.Time } type Protocol struct { BindAddress string // mattermost, slack + Buffer int // api IconURL string // mattermost, slack IgnoreNicks string // all protocols Jid string // xmpp @@ -79,6 +83,7 @@ type SameChannelGateway struct { } type Config struct { + Api map[string]Protocol IRC map[string]Protocol Mattermost map[string]Protocol Slack map[string]Protocol diff --git a/changelog.md b/changelog.md index b45a35d8..78015f90 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # v0.9.3-dev +## New features +* API: rest interface to read / post messages (see API section in matterbridge.toml.sample) + ## Bugfix * slack: fix receiving messages from private channels #118 * slack: fix echo when using webhooks #119 diff --git a/gateway/gateway.go b/gateway/gateway.go index f965f8a6..5e85926e 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -86,6 +86,7 @@ func (gw *Gateway) handleReceive() { } } if !gw.ignoreMessage(&msg) { + msg.Timestamp = time.Now() for _, br := range gw.Bridges { gw.handleMessage(msg, br) } @@ -165,6 +166,10 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) { } log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel) gw.modifyUsername(&msg, dest) + // for api we need originchannel as channel + if dest.Protocol == "api" { + msg.Channel = originchannel + } err := dest.Send(msg) if err != nil { fmt.Println(err) @@ -199,6 +204,7 @@ func (gw *Gateway) modifyMessage(msg *config.Message, dest *bridge.Bridge) { func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) { br := gw.Bridges[msg.Account] + msg.Protocol = br.Protocol nick := gw.Config.General.RemoteNickFormat if nick == "" { nick = dest.Config.RemoteNickFormat diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index e251d0e0..0fa95d0b 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -486,6 +486,28 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> " #OPTIONAL (default false) ShowJoinPart=false +################################################################### +#API +################################################################### +[api] +#You can configure multiple API hooks +#In this example we use [api.local] +#REQUIRED + +[api.local] +#Address to listen on for API +#REQUIRED +BindAddress="127.0.0.1:4242" + +#Amount of messages to keep in memory +Buffer=1000 + +#RemoteNickFormat defines how remote users appear on this bridge +#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. +#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge +#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge +#OPTIONAL (default empty) +RemoteNickFormat="{NICK}" ################################################################### #General configuration @@ -572,6 +594,13 @@ enable=true #OPTIONAL - your irc channel key key="yourkey" + #API example + #[[gateway.inout]] + #account="api.local" + #channel="api" + #To send data to the api: + #curl -XPOST -H 'Content-Type: application/json' -d '{"text":"test","username":"randomuser"}' http://localhost:4242/api/message + #If you want to do a 1:1 mapping between protocols where the channelnames are the same #e.g. slack and mattermost you can use the samechannelgateway configuration #the example configuration below send messages from channel testing on mattermost to