Add initial zulip support

This commit is contained in:
Wim 2018-05-07 21:35:48 +02:00
parent 1605fbc012
commit 45296100df
5 changed files with 262 additions and 1 deletions

View File

@ -7,7 +7,7 @@ Click on one of the badges below to join the chat
![matterbridge.gif](https://github.com/42wim/matterbridge/blob/master/img/matterbridge.gif) ![matterbridge.gif](https://github.com/42wim/matterbridge/blob/master/img/matterbridge.gif)
Simple bridge between IRC, XMPP, Gitter, Mattermost, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp), Matrix, Steam and ssh-chat Simple bridge between IRC, XMPP, Gitter, Mattermost, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp), Matrix, Steam, ssh-chat and Zulip
Has a REST API. Has a REST API.
Minecraft server chat support via [MatterLink](https://github.com/elytra/MatterLink) Minecraft server chat support via [MatterLink](https://github.com/elytra/MatterLink)
@ -62,6 +62,7 @@ Accounts to one of the supported bridges
* [Steam](https://store.steampowered.com/) * [Steam](https://store.steampowered.com/)
* [Twitch](https://twitch.tv) * [Twitch](https://twitch.tv)
* [Ssh-chat](https://github.com/shazow/ssh-chat) * [Ssh-chat](https://github.com/shazow/ssh-chat)
* [Zulip](https://zulipchat.com)
# Screenshots # Screenshots
See https://github.com/42wim/matterbridge/wiki See https://github.com/42wim/matterbridge/wiki
@ -189,6 +190,7 @@ Matterbridge wouldn't exist without these libraries:
* echo - https://github.com/labstack/echo * echo - https://github.com/labstack/echo
* gitter - https://github.com/sromku/go-gitter * gitter - https://github.com/sromku/go-gitter
* gops - https://github.com/google/gops * gops - https://github.com/google/gops
* gozulipbot - https://github.com/ifo/gozulipbot
* irc - https://github.com/lrstanley/girc * irc - https://github.com/lrstanley/girc
* mattermost - https://github.com/mattermost/platform * mattermost - https://github.com/mattermost/platform
* matrix - https://github.com/matrix-org/gomatrix * matrix - https://github.com/matrix-org/gomatrix

View File

@ -107,6 +107,7 @@ type Protocol struct {
StripNick bool // all protocols StripNick bool // all protocols
Team string // mattermost Team string // mattermost
Token string // gitter, slack, discord, api Token string // gitter, slack, discord, api
Topic string // zulip
URL string // mattermost, slack // DEPRECATED URL string // mattermost, slack // DEPRECATED
UseAPI bool // mattermost, slack UseAPI bool // mattermost, slack
UseSASL bool // IRC UseSASL bool // IRC
@ -159,6 +160,7 @@ type ConfigValues struct {
Telegram map[string]Protocol Telegram map[string]Protocol
Rocketchat map[string]Protocol Rocketchat map[string]Protocol
Sshchat map[string]Protocol Sshchat map[string]Protocol
Zulip map[string]Protocol
General Protocol General Protocol
Gateway []Gateway Gateway []Gateway
SameChannelGateway []SameChannelGateway SameChannelGateway []SameChannelGateway

170
bridge/zulip/zulip.go Normal file
View File

@ -0,0 +1,170 @@
package bzulip
import (
"encoding/json"
"io/ioutil"
"strconv"
"time"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
gzb "github.com/matterbridge/gozulipbot"
)
type Bzulip struct {
q *gzb.Queue
bot *gzb.Bot
streams map[int]string
*bridge.Config
}
func New(cfg *bridge.Config) bridge.Bridger {
return &Bzulip{Config: cfg, streams: make(map[int]string)}
}
func (b *Bzulip) Connect() error {
bot := gzb.Bot{APIKey: b.GetString("token"), APIURL: b.GetString("server") + "/api/v1/", Email: b.GetString("login")}
bot.Init()
q, err := bot.RegisterAll()
b.q = q
b.bot = &bot
if err != nil {
b.Log.Errorf("Connect() %#v", err)
return err
}
// init stream
b.getChannel(0)
b.Log.Info("Connection succeeded")
go b.handleQueue()
return nil
}
func (b *Bzulip) Disconnect() error {
return nil
}
func (b *Bzulip) JoinChannel(channel config.ChannelInfo) error {
return nil
}
func (b *Bzulip) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
// Delete message
if msg.Event == config.EVENT_MSG_DELETE {
if msg.ID == "" {
return "", nil
}
_, err := b.bot.UpdateMessage(msg.ID, "")
return "", err
}
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
b.sendMessage(rmsg)
}
if len(msg.Extra["file"]) > 0 {
return b.handleUploadFile(&msg)
}
}
// edit the message if we have a msg ID
if msg.ID != "" {
_, err := b.bot.UpdateMessage(msg.ID, msg.Username+msg.Text)
return "", err
}
// Post normal message
return b.sendMessage(msg)
}
func (b *Bzulip) getChannel(id int) string {
if name, ok := b.streams[id]; ok {
return name
}
streams, err := b.bot.GetRawStreams()
if err != nil {
b.Log.Errorf("getChannel: %#v", err)
return ""
}
for _, stream := range streams.Streams {
b.streams[stream.StreamID] = stream.Name
}
if name, ok := b.streams[id]; ok {
return name
}
return ""
}
func (b *Bzulip) handleQueue() error {
for {
messages, _ := b.q.GetEvents()
for _, m := range messages {
b.Log.Debugf("== Receiving %#v", m)
// ignore our own messages
if m.SenderEmail == b.GetString("login") {
continue
}
rmsg := config.Message{Username: m.SenderFullName, Text: m.Content, Channel: b.getChannel(m.StreamID), Account: b.Account, UserID: strconv.Itoa(m.SenderID), Avatar: m.AvatarURL}
b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
b.q.LastEventID = m.ID
}
time.Sleep(time.Second * 3)
}
}
func (b *Bzulip) sendMessage(msg config.Message) (string, error) {
topic := "matterbridge"
if b.GetString("topic") != "" {
topic = b.GetString("topic")
}
m := gzb.Message{
Stream: msg.Channel,
Topic: topic,
Content: msg.Username + msg.Text,
}
resp, err := b.bot.Message(m)
if err != nil {
return "", err
}
if resp != nil {
defer resp.Body.Close()
res, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var jr struct {
ID int `json:"id"`
}
err = json.Unmarshal(res, &jr)
if err != nil {
return "", err
}
return strconv.Itoa(jr.ID), nil
}
return "", nil
}
func (b *Bzulip) handleUploadFile(msg *config.Message) (string, error) {
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {
msg.Text += fi.Comment + ": "
}
if fi.URL != "" {
msg.Text = fi.URL
if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL
}
}
_, err := b.sendMessage(*msg)
if err != nil {
return "", err
}
}
return "", nil
}

View File

@ -17,6 +17,7 @@ import (
"github.com/42wim/matterbridge/bridge/steam" "github.com/42wim/matterbridge/bridge/steam"
"github.com/42wim/matterbridge/bridge/telegram" "github.com/42wim/matterbridge/bridge/telegram"
"github.com/42wim/matterbridge/bridge/xmpp" "github.com/42wim/matterbridge/bridge/xmpp"
"github.com/42wim/matterbridge/bridge/zulip"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
// "github.com/davecgh/go-spew/spew" // "github.com/davecgh/go-spew/spew"
"crypto/sha1" "crypto/sha1"
@ -62,6 +63,7 @@ var bridgeMap = map[string]bridge.Factory{
"steam": bsteam.New, "steam": bsteam.New,
"telegram": btelegram.New, "telegram": btelegram.New,
"xmpp": bxmpp.New, "xmpp": bxmpp.New,
"zulip": bzulip.New,
} }
func init() { func init() {

View File

@ -1155,6 +1155,90 @@ StripNick=false
#OPTIONAL (default false) #OPTIONAL (default false)
ShowTopicChange=false ShowTopicChange=false
###################################################################
#zulip section
###################################################################
[zulip]
#You can configure multiple servers "[zulip.name]" or "[zulip.name2]"
#In this example we use [zulip.streamchat]
#REQUIRED
[zulip.streamchat]
#Token to connect with zulip API (called bot API key in Settings - Your bots)
#REQUIRED
Token="Yourtokenhere"
#Username of the bot, normally called yourbot-bot@yourserver.zulipchat.com
#See username in Settings - Your bots
#REQUIRED
Login="yourbot-bot@yourserver.zulipchat.com"
#Servername of your zulip instance
#REQUIRED
Server="https://yourserver.zulipchat.com"
#Topic of the messages matterbridge will use
#OPTIONAL (default "matterbridge")
Topic="matterbridge"
## RELOADABLE SETTINGS
## Settings below can be reloaded by editing the file
#Nicks you want to ignore.
#Messages from those users will not be sent to other bridges.
#OPTIONAL
IgnoreNicks="spammer1 spammer2"
#Messages you want to ignore.
#Messages matching these regexp will be ignored and not sent to other bridges
#See https://regex-golang.appspot.com/assets/html/index.html for more regex info
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
IgnoreMessages="^~~ badword"
#messages you want to replace.
#it replaces outgoing messages from the bridge.
#so you need to place it by the sending bridge definition.
#regular expressions supported
#some examples:
#this replaces cat => dog and sleep => awake
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
#this replaces every number with number. 123 => numbernumbernumber
#replacemessages=[ ["[0-9]","number"] ]
#optional (default empty)
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
#extra label that can be used in the RemoteNickFormat
#optional (default empty)
Label=""
#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 "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
#OPTIONAL (default empty)
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack
#OPTIONAL (default false)
ShowJoinPart=false
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
#It will strip other characters from the nick
#OPTIONAL (default false)
StripNick=false
#Enable to show topic changes from other bridges
#Only works hiding/show topic changes from slack bridge for now
#OPTIONAL (default false)
ShowTopicChange=false
################################################################### ###################################################################
#API #API
################################################################### ###################################################################
@ -1283,6 +1367,7 @@ enable=true
# - encrypted rooms are not supported in matrix # - encrypted rooms are not supported in matrix
#steam - chatid (a large number). #steam - chatid (a large number).
# The number in the URL when you click "enter chat room" in the browser # The number in the URL when you click "enter chat room" in the browser
#zulip - stream (without the #)
# #
#REQUIRED #REQUIRED
channel="#testing" channel="#testing"