4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-26 18:59:24 +00:00

Compare commits

...

26 Commits
v0.1 ... v0.3

Author SHA1 Message Date
Wim
a64deb1238 Update to v0.3 2016-03-03 22:24:08 +01:00
Wim
f914695801 Add support for slack username circumfix. Closes #10 2016-02-18 21:45:29 +01:00
304dc2e25f Merge pull request #8 from daysofwineandroses/master
Add PASS support as per RFC1459
2016-01-29 00:45:01 +01:00
fd74dca175 Add PASS support as per RFC1459
Provide a connection password via the protocol's PASS command.

Imported irc.go supports it as a simple parameter:
https://github.com/thoj/go-ircevent/blob/master/irc.go#L381

See https://tools.ietf.org/html/rfc1459#section-4.1 for full details.
2016-01-27 20:09:06 +01:00
Wim
c7ace91bf6 Add link to matterbridge-plus 2015-12-20 16:21:30 +01:00
Wim
9f07a2cfd5 Add support for multiple channels 2015-12-19 16:55:49 +01:00
Wim
0dc5e042d2 Add option to change receiving mattermost channel 2015-12-19 15:55:07 +01:00
Wim
f0a5d2396f Add option to specify configfile 2015-12-18 20:54:28 +01:00
Wim
bdac03f725 Add BindAddress option. Closes #4 2015-12-12 23:20:13 +01:00
Wim
c1f80383f7 Update to v0.2 2015-12-09 00:42:50 +01:00
Wim
bd7c1e3e3c Set type join_leave for irc JOIN/PART messages send to mattermost 2015-11-29 00:28:10 +01:00
Wim
5c1b02c7a3 Add support for Type and Attachments in incoming webhooks 2015-11-28 23:57:47 +01:00
Wim
38fce68609 Fix go get path 2015-10-30 09:57:23 +01:00
Wim
90f276863b Add DisableServer option 2015-10-28 17:19:32 +01:00
Wim
5282cdaccd Remove markdown for giphy 2015-10-28 00:16:42 +01:00
Wim
008ea94b53 Add giphy support. !gif <query> 2015-10-28 00:04:57 +01:00
Wim
693f1946b7 Fix multiline messages 2015-10-27 11:25:21 +01:00
Wim
8b6a00d1c5 Add SkipTLSVerify option for mattermost, allows selfsigned certificates 2015-10-25 01:00:19 +02:00
Wim
43738dbc89 Refactor and IconURL support 2015-10-24 18:44:45 +02:00
Wim
6feccd4c6c Add support for outgoing webhook token 2015-10-24 18:05:10 +02:00
Wim
25d72a7e31 Add some validation for incoming connections 2015-10-24 17:44:14 +02:00
Wim
523f6ffb80 Add support for NAMES 2015-10-24 17:25:18 +02:00
Wim
b346ac868b Add support for JOIN, PART and CTCP_ACTION 2015-10-24 16:39:01 +02:00
Wim
d0cda03478 Add matterhook package info 2015-10-23 23:31:14 +02:00
Wim
19b3145bd1 Add mattermost requirement 2015-10-23 23:11:16 +02:00
Wim
a0d1fc0d6a Add link to binaries 2015-10-23 23:02:14 +02:00
5 changed files with 248 additions and 38 deletions

View File

@ -3,14 +3,19 @@
Simple bridge between mattermost and IRC. Uses the in/outgoing webhooks.
Relays public channel messages between mattermost and IRC.
Work in progress.
Requires mattermost 1.2.0+
There is also [matterbridge-plus] (https://github.com/42wim/matterbridge-plus) which uses the mattermost API and needs a dedicated user (bot). But requires no incoming/outgoing webhook setup.
## binaries
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/tag/v0.3)
## building
Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
```
cd $GOPATH
go get https://github.com/42wim/matterbridge
go get github.com/42wim/matterbridge
```
You should now have matterbridge binary in the bin directory:
@ -23,7 +28,12 @@ matterbridge
## running
1) Copy the matterbridge.conf.sample to matterbridge.conf in the same directory as the matterbridge binary.
2) Edit matterbridge.conf with the settings for your environment. See below for more config information.
3) Now you can run matterbridge.
3) Now you can run matterbridge.
```
Usage of matterbridge:
-conf="matterbridge.conf": config file
```
Matterbridge will:
* start a webserver listening on the port specified in the configuration.
@ -32,7 +42,7 @@ Matterbridge will:
## config
### matterbridge
matterbridge looks for matterbridge.conf in current directory.
matterbridge looks for matterbridge.conf in current directory. (use -conf to specify another file)
Look at matterbridge.conf.sample for an example
@ -45,12 +55,35 @@ UseTLS=false
SkipTLSVerify=true
nick="matterbot"
channel="#matterbridge"
UseSlackCircumfix=false
[mattermost]
#url is your incoming webhook url (account settings - integrations - incoming webhooks)
url="http://mattermost.yourdomain.com/hooks/incomingwebhookkey"
#port the bridge webserver will listen on
port=9999
#address the webserver will bind to
BindAddress="0.0.0.0"
showjoinpart=true #show irc users joining and parting
#the token you get from the outgoing webhook in mattermost. If empty no token check will be done.
#if you use multiple IRC channel (see below, this must be empty!)
token=yourtokenfrommattermost
#disable certificate checking (selfsigned certificates)
#SkipTLSVerify=true
#multiple channel config
#token you can find in your outgoing webhook
[Token "outgoingwebhooktoken1"]
IRCChannel="#off-topic"
MMChannel="off-topic"
[Token "outgoingwebhooktoken2"]
IRCChannel="#testing"
MMChannel="testing"
[general]
#request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key
GiphyApiKey="dc6zaTOxFJmzC"
```
### mattermost

View File

@ -8,16 +8,31 @@ import (
type Config struct {
IRC struct {
UseTLS bool
SkipTLSVerify bool
Server string
Port int
Nick string
Channel string
UseTLS bool
SkipTLSVerify bool
Server string
Port int
Nick string
Password string
Channel string
UseSlackCircumfix bool
}
Mattermost struct {
URL string
Port int
URL string
Port int
ShowJoinPart bool
Token string
IconURL string
SkipTLSVerify bool
BindAddress string
Channel string
}
Token map[string]*struct {
IRCChannel string
MMChannel string
}
General struct {
GiphyAPIKey string
}
}

View File

@ -5,7 +5,28 @@ UseTLS=false
SkipTLSVerify=true
nick="matterbot"
channel="#matterbridge"
UseSlackCircumfix=false
[mattermost]
url="http://yourdomain/hooks/yourhookkey"
port=9999
showjoinpart=true
#remove token when using multiple channels!
token=yourtokenfrommattermost
IconURL="http://youricon.png"
#SkipTLSVerify=true
#BindAddress="0.0.0.0"
[general]
GiphyAPIKey=dc6zaTOxFJmzC
#multiple channel config
#token you can find in your outgoing webhook
[Token "outgoingwebhooktoken1"]
IRCChannel="#off-topic"
MMChannel="off-topic"
[Token "outgoingwebhooktoken2"]
IRCChannel="#testing"
MMChannel="testing"

View File

@ -2,23 +2,36 @@ package main
import (
"crypto/tls"
"flag"
"github.com/42wim/matterbridge/matterhook"
"github.com/peterhellberg/giphy"
"github.com/thoj/go-ircevent"
"log"
"strconv"
"strings"
"time"
)
type Bridge struct {
i *irc.Connection
m *matterhook.Client
i *irc.Connection
m *matterhook.Client
cmap map[string]string
*Config
}
func NewBridge(name string, config *Config) *Bridge {
b := &Bridge{}
b.Config = config
b.m = matterhook.New(b.Config.Mattermost.URL, matterhook.Config{Port: b.Config.Mattermost.Port})
b.cmap = make(map[string]string)
if len(b.Config.Token) > 0 {
for _, val := range b.Config.Token {
b.cmap[val.IRCChannel] = val.MMChannel
}
}
b.m = matterhook.New(b.Config.Mattermost.URL,
matterhook.Config{Port: b.Config.Mattermost.Port, Token: b.Config.Mattermost.Token,
InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify,
BindAddress: b.Config.Mattermost.BindAddress})
b.i = b.createIRC(name)
go b.handleMatter()
return b
@ -28,29 +41,123 @@ func (b *Bridge) createIRC(name string) *irc.Connection {
i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick)
i.UseTLS = b.Config.IRC.UseTLS
i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify}
if b.Config.IRC.Password != "" {
i.Password = b.Config.IRC.Password
}
i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port))
time.Sleep(time.Second)
log.Println("Joining", b.Config.IRC.Channel, "as", b.Config.IRC.Nick)
i.Join(b.Config.IRC.Channel)
for _, val := range b.Config.Token {
log.Println("Joining", val.IRCChannel, "as", b.Config.IRC.Nick)
i.Join(val.IRCChannel)
}
i.AddCallback("PRIVMSG", b.handlePrivMsg)
i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
if b.Config.Mattermost.ShowJoinPart {
i.AddCallback("JOIN", b.handleJoinPart)
i.AddCallback("PART", b.handleJoinPart)
}
//i.AddCallback("353", b.handleOther)
return i
}
func (b *Bridge) handlePrivMsg(event *irc.Event) {
matterMessage := matterhook.OMessage{}
matterMessage.Text = event.Message()
matterMessage.UserName = "irc-" + event.Nick
b.m.Send(matterMessage)
msg := ""
if event.Code == "CTCP_ACTION" {
msg = event.Nick + " "
}
msg += event.Message()
b.Send("irc-"+event.Nick, msg, b.getMMChannel(event.Arguments[0]))
}
func (b *Bridge) handleMatter() {
for {
message := b.m.Receive()
b.i.Privmsg(b.Config.IRC.Channel, message.UserName+": "+message.Text)
func (b *Bridge) handleJoinPart(event *irc.Event) {
b.Send(b.Config.IRC.Nick, "irc-"+event.Nick+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0]))
//b.SendType(b.Config.IRC.Nick, "irc-"+event.Nick+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0]), "join_leave")
}
func (b *Bridge) handleOther(event *irc.Event) {
switch event.Code {
case "353":
log.Println("handleOther", b.getMMChannel(event.Arguments[0]))
b.Send(b.Config.IRC.Nick, event.Message()+" currently on IRC", b.getMMChannel(event.Arguments[0]))
}
}
func (b *Bridge) Send(nick string, message string, channel string) error {
return b.SendType(nick, message, channel, "")
}
func (b *Bridge) SendType(nick string, message string, channel string, mtype string) error {
matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL}
matterMessage.Channel = channel
matterMessage.UserName = nick
matterMessage.Text = message
matterMessage.Type = mtype
err := b.m.Send(matterMessage)
if err != nil {
log.Println(err)
return err
}
return nil
}
func (b *Bridge) handleMatter() {
var username string
for {
message := b.m.Receive()
username = message.UserName + ": "
if b.Config.IRC.UseSlackCircumfix {
username = "<" + message.UserName + "> "
}
cmd := strings.Fields(message.Text)[0]
switch cmd {
case "!users":
log.Println("received !users from", message.UserName)
b.i.SendRaw("NAMES " + b.getIRCChannel(message.Token))
case "!gif":
message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1)))
b.Send(b.Config.IRC.Nick, message.Text, b.getIRCChannel(message.Token))
}
texts := strings.Split(message.Text, "\n")
for _, text := range texts {
b.i.Privmsg(b.getIRCChannel(message.Token), username+text)
}
}
}
func (b *Bridge) giphyRandom(query []string) string {
g := giphy.DefaultClient
if b.Config.General.GiphyAPIKey != "" {
g.APIKey = b.Config.General.GiphyAPIKey
}
res, err := g.Random(query)
if err != nil {
return "error"
}
return res.Data.FixedHeightDownsampledURL
}
func (b *Bridge) getMMChannel(ircChannel string) string {
mmchannel, ok := b.cmap[ircChannel]
if !ok {
mmchannel = b.Config.Mattermost.Channel
}
return mmchannel
}
func (b *Bridge) getIRCChannel(token string) string {
ircchannel := b.Config.IRC.Channel
_, ok := b.Config.Token[token]
if ok {
ircchannel = b.Config.Token[token].IRCChannel
}
return ircchannel
}
func main() {
NewBridge("matterbot", NewConfig("matterbridge.conf"))
flagConfig := flag.String("conf", "matterbridge.conf", "config file")
flag.Parse()
NewBridge("matterbot", NewConfig(*flagConfig))
select {}
}

View File

@ -1,7 +1,9 @@
//Package matterhook provides interaction with mattermost incoming/outgoing webhooks
package matterhook
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/gorilla/schema"
@ -14,11 +16,13 @@ import (
// OMessage for mattermost incoming webhook. (send to mattermost)
type OMessage struct {
Channel string `json:"channel,omitempty"`
IconURL string `json:"icon_url,omitempty"`
IconEmoji string `json:"icon_emoji,omitempty"`
UserName string `json:"username,omitempty"`
Text string `json:"text"`
Channel string `json:"channel,omitempty"`
IconURL string `json:"icon_url,omitempty"`
IconEmoji string `json:"icon_emoji,omitempty"`
UserName string `json:"username,omitempty"`
Text string `json:"text"`
Attachments interface{} `json:"attachments,omitempty"`
Type string `json:"type,omitempty"`
}
// IMessage for mattermost outgoing webhook. (received from mattermost)
@ -38,23 +42,36 @@ type IMessage struct {
// Client for Mattermost.
type Client struct {
url string
In chan IMessage
Out chan OMessage
Url string // URL for incoming webhooks on mattermost.
In chan IMessage
Out chan OMessage
httpclient *http.Client
Config
}
// Config for client.
type Config struct {
Port int
Port int // Port to listen on.
BindAddress string // Address to listen on
Token string // Only allow this token from Mattermost. (Allow everything when empty)
InsecureSkipVerify bool // disable certificate checking
DisableServer bool // Do not start server for outgoing webhooks from Mattermost.
}
// New Mattermost client.
func New(url string, config Config) *Client {
c := &Client{url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config}
c := &Client{Url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config}
if c.Port == 0 {
c.Port = 9999
}
go c.StartServer()
c.BindAddress += ":"
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify},
}
c.httpclient = &http.Client{Transport: tr}
if !c.DisableServer {
go c.StartServer()
}
return c
}
@ -62,14 +79,19 @@ func New(url string, config Config) *Client {
func (c *Client) StartServer() {
mux := http.NewServeMux()
mux.Handle("/", c)
log.Printf("Listening on http://0.0.0.0:%v...\n", c.Port)
if err := http.ListenAndServe((":" + strconv.Itoa(c.Port)), mux); err != nil {
log.Printf("Listening on http://%v:%v...\n", c.BindAddress, c.Port)
if err := http.ListenAndServe((c.BindAddress + strconv.Itoa(c.Port)), mux); err != nil {
log.Fatal(err)
}
}
// ServeHTTP implementation.
func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
log.Println("invalid " + r.Method + " connection from " + r.RemoteAddr)
http.NotFound(w, r)
return
}
msg := IMessage{}
err := r.ParseForm()
if err != nil {
@ -85,6 +107,18 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
return
}
if msg.Token == "" {
log.Println("no token from " + r.RemoteAddr)
http.NotFound(w, r)
return
}
if c.Token != "" {
if msg.Token != c.Token {
log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr)
http.NotFound(w, r)
return
}
}
c.In <- msg
}
@ -104,7 +138,7 @@ func (c *Client) Send(msg OMessage) error {
if err != nil {
return err
}
resp, err := http.Post(c.url, "application/json", bytes.NewReader(buf))
resp, err := c.httpclient.Post(c.Url, "application/json", bytes.NewReader(buf))
if err != nil {
return err
}