forked from lug/matterbridge
		
	Compare commits
	
		
			61 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6e410b096e | ||
| 
						 | 
					f9e5994348 | ||
| 
						 | 
					ee77272cfd | ||
| 
						 | 
					16ed2aca6a | ||
| 
						 | 
					0f530e7902 | ||
| 
						 | 
					4ed66ce20e | ||
| 
						 | 
					b30e85836e | ||
| 
						 | 
					e449a97bd0 | ||
| 
						 | 
					39043f3fa4 | ||
| 
						 | 
					12389d602e | ||
| 
						 | 
					44144587a0 | ||
| 
						 | 
					d0a30e354b | ||
| 
						 | 
					c261dc89d5 | ||
| 
						 | 
					c2c135bca2 | ||
| 
						 | 
					eb20cb237d | ||
| 
						 | 
					106404d32f | ||
| 
						 | 
					e06efbad9f | ||
| 
						 | 
					3311c7f923 | ||
| 
						 | 
					3a6c655dfb | ||
| 
						 | 
					e11d786775 | ||
| 
						 | 
					889b6debc4 | ||
| 
						 | 
					9cb3413d9c | ||
| 
						 | 
					131826e1d1 | ||
| 
						 | 
					96e21dd051 | ||
| 
						 | 
					32e5f396e7 | ||
| 
						 | 
					6c6000dbbd | ||
| 
						 | 
					24defcb970 | ||
| 
						 | 
					a1a11a88b3 | ||
| 
						 | 
					a997ae29ad | ||
| 
						 | 
					ff94796700 | ||
| 
						 | 
					1f72ca4c4e | ||
| 
						 | 
					46faad8b57 | ||
| 
						 | 
					30f30364d5 | ||
| 
						 | 
					073d90da88 | ||
| 
						 | 
					c769e23a9a | ||
| 
						 | 
					9db48f4794 | ||
| 
						 | 
					911c597377 | ||
| 
						 | 
					28244ffd9a | ||
| 
						 | 
					3e38c7945c | ||
| 
						 | 
					79ffb76f6e | ||
| 
						 | 
					5fe4b749cf | ||
| 
						 | 
					6991d85da9 | ||
| 
						 | 
					c1c187a1ab | ||
| 
						 | 
					055d12e3ef | ||
| 
						 | 
					b49429d722 | ||
| 
						 | 
					815c7f8d64 | ||
| 
						 | 
					c879f79456 | ||
| 
						 | 
					3bc25f4707 | ||
| 
						 | 
					300cfe044a | ||
| 
						 | 
					fb586f4a96 | ||
| 
						 | 
					ced371bece | ||
| 
						 | 
					a87cac1982 | ||
| 
						 | 
					8fb5c7afa6 | ||
| 
						 | 
					aceb830378 | ||
| 
						 | 
					0f2976c5ce | ||
| 
						 | 
					78b17977c5 | ||
| 
						 | 
					6ec77e06ea | ||
| 
						 | 
					e48db67649 | ||
| 
						 | 
					e03f331f55 | ||
| 
						 | 
					ff5aeeb1e1 | ||
| 
						 | 
					33844fa60c | 
							
								
								
									
										11
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
FROM alpine:edge
 | 
			
		||||
ENTRYPOINT ["/bin/matterbridge"]
 | 
			
		||||
 | 
			
		||||
COPY . /go/src/github.com/42wim/matterbridge
 | 
			
		||||
RUN apk update && apk add go git gcc musl-dev ca-certificates \
 | 
			
		||||
        && cd /go/src/github.com/42wim/matterbridge \
 | 
			
		||||
        && export GOPATH=/go \
 | 
			
		||||
        && go get \
 | 
			
		||||
        && go build -o /bin/matterbridge \
 | 
			
		||||
        && rm -rf /go \
 | 
			
		||||
        && apk del --purge git go gcc musl-dev
 | 
			
		||||
							
								
								
									
										124
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								README.md
									
									
									
									
									
								
							@@ -1,14 +1,40 @@
 | 
			
		||||
# matterbridge
 | 
			
		||||
 | 
			
		||||
Simple bridge between mattermost and IRC. Uses the in/outgoing webhooks.  
 | 
			
		||||
Relays public channel messages between mattermost and IRC.  
 | 
			
		||||
Simple bridge between mattermost, IRC, XMPP and Gitter
 | 
			
		||||
 | 
			
		||||
Requires mattermost 1.2.0+
 | 
			
		||||
* Relays public channel messages between mattermost, IRC, XMPP and Gitter. Pick and mix.
 | 
			
		||||
* Supports multiple channels.
 | 
			
		||||
* Matterbridge -plus also works with private groups on your mattermost.
 | 
			
		||||
 | 
			
		||||
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. 
 | 
			
		||||
Look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for documentation and an example.
 | 
			
		||||
 | 
			
		||||
## Changelog
 | 
			
		||||
Since v0.6.1 support for XMPP, Gitter and Slack is added. More details in [changelog.md] (https://github.com/42wim/matterbridge/blob/master/changelog.md)
 | 
			
		||||
 | 
			
		||||
## Requirements:
 | 
			
		||||
Accounts to one of the supported bridges
 | 
			
		||||
* [Mattermost] (https://github.com/mattermost/platform/)
 | 
			
		||||
* [IRC] (http://www.mirc.com/servers.html)
 | 
			
		||||
* [XMPP] (https://jabber.org)
 | 
			
		||||
* [Gitter] (https://gitter.im)
 | 
			
		||||
 | 
			
		||||
## binaries
 | 
			
		||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/tag/v0.4)
 | 
			
		||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
 | 
			
		||||
* For use with mattermost 3.3.0+ [v0.6.1](https://github.com/42wim/matterircd/releases/tag/v0.6.1)
 | 
			
		||||
* For use with mattermost 3.0.0-3.2.0 [v0.5.0](https://github.com/42wim/matterircd/releases/tag/v0.5.0)
 | 
			
		||||
 | 
			
		||||
## Compatibility
 | 
			
		||||
### Mattermost 
 | 
			
		||||
* Matterbridge v0.6.1 works with mattermost 3.3.0 and higher [3.3.0 release](https://github.com/mattermost/platform/releases/tag/v3.3.0)
 | 
			
		||||
* Matterbridge v0.5.0 works with mattermost 3.0.0 - 3.2.0 [3.2.0 release](https://github.com/mattermost/platform/releases/tag/v3.2.0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Webhooks version
 | 
			
		||||
* Configured incoming/outgoing [webhooks](https://www.mattermost.org/webhooks/) on your mattermost instance.
 | 
			
		||||
 | 
			
		||||
#### Plus (API) version
 | 
			
		||||
* A dedicated user(bot) on your mattermost instance.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## building
 | 
			
		||||
Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
 | 
			
		||||
@@ -31,73 +57,26 @@ matterbridge
 | 
			
		||||
3) Now you can run matterbridge. 
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Usage of matterbridge:
 | 
			
		||||
  -conf="matterbridge.conf": config file
 | 
			
		||||
Usage of ./matterbridge:
 | 
			
		||||
  -conf string
 | 
			
		||||
        config file (default "matterbridge.conf")
 | 
			
		||||
  -debug
 | 
			
		||||
        enable debug
 | 
			
		||||
  -plus
 | 
			
		||||
        running using API instead of webhooks (deprecated, set Plus flag in [general] config)
 | 
			
		||||
  -version
 | 
			
		||||
        show version
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Matterbridge will:
 | 
			
		||||
* start a webserver listening on the port specified in the configuration.
 | 
			
		||||
* connect to specified irc server and channel.
 | 
			
		||||
* send messages from mattermost to irc and vice versa, messages in mattermost will appear with irc-nick
 | 
			
		||||
 | 
			
		||||
## config
 | 
			
		||||
### matterbridge
 | 
			
		||||
matterbridge looks for matterbridge.conf in current directory. (use -conf to specify another file)
 | 
			
		||||
 | 
			
		||||
Look at matterbridge.conf.sample for an example
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[IRC]
 | 
			
		||||
server="irc.freenode.net"
 | 
			
		||||
port=6667
 | 
			
		||||
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
 | 
			
		||||
#whether to prefix messages from IRC to mattermost with the sender's nick. Useful if username overrides for incoming webhooks isn't enabled on the mattermost server
 | 
			
		||||
PrefixMessagesWithNick=false
 | 
			
		||||
#how to format the list of IRC nicks when displayed in mattermost. Possible options are "table" and "plain"
 | 
			
		||||
NickFormatter=plain
 | 
			
		||||
#how many nicks to list per row for formatters that support this
 | 
			
		||||
NicksPerRow=4
 | 
			
		||||
#Freenode nickserv
 | 
			
		||||
NickServNick="nickserv"
 | 
			
		||||
#Password for nickserv
 | 
			
		||||
NickServPassword="secret"
 | 
			
		||||
 | 
			
		||||
#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"
 | 
			
		||||
```
 | 
			
		||||
Look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for an example.
 | 
			
		||||
 | 
			
		||||
### mattermost
 | 
			
		||||
You'll have to configure the incoming en outgoing webhooks. 
 | 
			
		||||
#### webhooks version
 | 
			
		||||
You'll have to configure the incoming and outgoing webhooks. 
 | 
			
		||||
 | 
			
		||||
* incoming webhooks
 | 
			
		||||
Go to "account settings" - integrations - "incoming webhooks".  
 | 
			
		||||
@@ -108,5 +87,20 @@ This URL should be set in the matterbridge.conf in the [mattermost] section (see
 | 
			
		||||
Go to "account settings" - integrations - "outgoing webhooks".  
 | 
			
		||||
Choose a channel (the same as the one from incoming webhooks) and fill in the address and port of the server matterbridge will run on.  
 | 
			
		||||
 | 
			
		||||
e.g. http://192.168.1.1:9999 (9999 is the port specified in [mattermost] section of matterbridge.conf)
 | 
			
		||||
e.g. http://192.168.1.1:9999 (192.168.1.1:9999 is the BindAddress specified in [mattermost] section of matterbridge.conf)
 | 
			
		||||
 | 
			
		||||
#### plus version
 | 
			
		||||
You'll have to create a new dedicated user on your mattermost instance.
 | 
			
		||||
Specify the login and password in [mattermost] section of matterbridge.conf
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
Please look at [matterbridge.conf.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.conf.sample) for more information first. 
 | 
			
		||||
### Mattermost doesn't show the IRC nicks
 | 
			
		||||
If you're running the webhooks version, this can be fixed by either:
 | 
			
		||||
* enabling "override usernames". See [mattermost documentation](http://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks)
 | 
			
		||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.conf.
 | 
			
		||||
 | 
			
		||||
If you're running the plus version you'll need to:
 | 
			
		||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.conf.
 | 
			
		||||
 | 
			
		||||
Also look at the ```RemoteNickFormat``` setting.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										151
									
								
								bridge/bridge.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								bridge/bridge.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
package bridge
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	//"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/gitter"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/irc"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/mattermost"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/slack"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/xmpp"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bridge struct {
 | 
			
		||||
	*config.Config
 | 
			
		||||
	Source      string
 | 
			
		||||
	Bridges     []Bridger
 | 
			
		||||
	Channels    []map[string]string
 | 
			
		||||
	ignoreNicks map[string][]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Bridger interface {
 | 
			
		||||
	Send(msg config.Message) error
 | 
			
		||||
	Name() string
 | 
			
		||||
	Connect() error
 | 
			
		||||
	//Command(cmd string) string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewBridge(cfg *config.Config) error {
 | 
			
		||||
	c := make(chan config.Message)
 | 
			
		||||
	b := &Bridge{}
 | 
			
		||||
	b.Config = cfg
 | 
			
		||||
	if cfg.IRC.Enable {
 | 
			
		||||
		b.Bridges = append(b.Bridges, birc.New(cfg, c))
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.Mattermost.Enable {
 | 
			
		||||
		b.Bridges = append(b.Bridges, bmattermost.New(cfg, c))
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.Xmpp.Enable {
 | 
			
		||||
		b.Bridges = append(b.Bridges, bxmpp.New(cfg, c))
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.Gitter.Enable {
 | 
			
		||||
		b.Bridges = append(b.Bridges, bgitter.New(cfg, c))
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.Slack.Enable {
 | 
			
		||||
		b.Bridges = append(b.Bridges, bslack.New(cfg, c))
 | 
			
		||||
	}
 | 
			
		||||
	if len(b.Bridges) < 2 {
 | 
			
		||||
		log.Fatalf("only %d sections enabled. Need at least 2 sections enabled (eg [IRC] and [mattermost]", len(b.Bridges))
 | 
			
		||||
	}
 | 
			
		||||
	for _, br := range b.Bridges {
 | 
			
		||||
		br.Connect()
 | 
			
		||||
	}
 | 
			
		||||
	b.mapChannels()
 | 
			
		||||
	b.mapIgnores()
 | 
			
		||||
	b.handleReceive(c)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleReceive(c chan config.Message) {
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case msg := <-c:
 | 
			
		||||
			for _, br := range b.Bridges {
 | 
			
		||||
				b.handleMessage(msg, br)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) mapChannels() error {
 | 
			
		||||
	for _, val := range b.Config.Channel {
 | 
			
		||||
		m := make(map[string]string)
 | 
			
		||||
		m["irc"] = val.IRC
 | 
			
		||||
		m["mattermost"] = val.Mattermost
 | 
			
		||||
		m["xmpp"] = val.Xmpp
 | 
			
		||||
		m["gitter"] = val.Gitter
 | 
			
		||||
		m["slack"] = val.Slack
 | 
			
		||||
		b.Channels = append(b.Channels, m)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) mapIgnores() {
 | 
			
		||||
	m := make(map[string][]string)
 | 
			
		||||
	m["irc"] = strings.Fields(b.Config.IRC.IgnoreNicks)
 | 
			
		||||
	m["mattermost"] = strings.Fields(b.Config.Mattermost.IgnoreNicks)
 | 
			
		||||
	m["xmpp"] = strings.Fields(b.Config.Xmpp.IgnoreNicks)
 | 
			
		||||
	m["gitter"] = strings.Fields(b.Config.Gitter.IgnoreNicks)
 | 
			
		||||
	m["slack"] = strings.Fields(b.Config.Slack.IgnoreNicks)
 | 
			
		||||
	b.ignoreNicks = m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) getDestChannel(msg *config.Message, dest string) string {
 | 
			
		||||
	for _, v := range b.Channels {
 | 
			
		||||
		if v[msg.Origin] == msg.Channel {
 | 
			
		||||
			return v[dest]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleMessage(msg config.Message, dest Bridger) {
 | 
			
		||||
	if b.ignoreMessage(&msg) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if dest.Name() != msg.Origin {
 | 
			
		||||
		msg.Channel = b.getDestChannel(&msg, dest.Name())
 | 
			
		||||
		if msg.Channel == "" {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		b.modifyMessage(&msg, dest.Name())
 | 
			
		||||
		log.Debugf("sending %#v from %s to %s", msg, msg.Origin, dest.Name())
 | 
			
		||||
		dest.Send(msg)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) ignoreMessage(msg *config.Message) bool {
 | 
			
		||||
	// should we discard messages ?
 | 
			
		||||
	for _, entry := range b.ignoreNicks[msg.Origin] {
 | 
			
		||||
		if msg.Username == entry {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setNickFormat(msg *config.Message, format string) {
 | 
			
		||||
	if format == "" {
 | 
			
		||||
		msg.Username = msg.Origin + "-" + msg.Username + ": "
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msg.Username = strings.Replace(format, "{NICK}", msg.Username, -1)
 | 
			
		||||
	msg.Username = strings.Replace(msg.Username, "{BRIDGE}", msg.Origin, -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) modifyMessage(msg *config.Message, dest string) {
 | 
			
		||||
	switch dest {
 | 
			
		||||
	case "irc":
 | 
			
		||||
		setNickFormat(msg, b.Config.IRC.RemoteNickFormat)
 | 
			
		||||
	case "gitter":
 | 
			
		||||
		setNickFormat(msg, b.Config.Gitter.RemoteNickFormat)
 | 
			
		||||
	case "xmpp":
 | 
			
		||||
		setNickFormat(msg, b.Config.Xmpp.RemoteNickFormat)
 | 
			
		||||
	case "mattermost":
 | 
			
		||||
		setNickFormat(msg, b.Config.Mattermost.RemoteNickFormat)
 | 
			
		||||
	case "slack":
 | 
			
		||||
		setNickFormat(msg, b.Config.Slack.RemoteNickFormat)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										107
									
								
								bridge/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								bridge/config/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"gopkg.in/gcfg.v1"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Text     string
 | 
			
		||||
	Channel  string
 | 
			
		||||
	Username string
 | 
			
		||||
	Origin   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	IRC struct {
 | 
			
		||||
		UseTLS           bool
 | 
			
		||||
		UseSASL          bool
 | 
			
		||||
		SkipTLSVerify    bool
 | 
			
		||||
		Server           string
 | 
			
		||||
		Nick             string
 | 
			
		||||
		Password         string
 | 
			
		||||
		Channel          string
 | 
			
		||||
		NickServNick     string
 | 
			
		||||
		NickServPassword string
 | 
			
		||||
		RemoteNickFormat string
 | 
			
		||||
		IgnoreNicks      string
 | 
			
		||||
		Enable           bool
 | 
			
		||||
	}
 | 
			
		||||
	Gitter struct {
 | 
			
		||||
		Enable           bool
 | 
			
		||||
		IgnoreNicks      string
 | 
			
		||||
		Nick             string
 | 
			
		||||
		RemoteNickFormat string
 | 
			
		||||
		Token            string
 | 
			
		||||
	}
 | 
			
		||||
	Mattermost struct {
 | 
			
		||||
		URL                    string
 | 
			
		||||
		ShowJoinPart           bool
 | 
			
		||||
		IconURL                string
 | 
			
		||||
		SkipTLSVerify          bool
 | 
			
		||||
		BindAddress            string
 | 
			
		||||
		Channel                string
 | 
			
		||||
		PrefixMessagesWithNick bool
 | 
			
		||||
		NicksPerRow            int
 | 
			
		||||
		NickFormatter          string
 | 
			
		||||
		Server                 string
 | 
			
		||||
		Team                   string
 | 
			
		||||
		Login                  string
 | 
			
		||||
		Password               string
 | 
			
		||||
		RemoteNickFormat       string
 | 
			
		||||
		IgnoreNicks            string
 | 
			
		||||
		NoTLS                  bool
 | 
			
		||||
		Enable                 bool
 | 
			
		||||
	}
 | 
			
		||||
	Slack struct {
 | 
			
		||||
		BindAddress            string
 | 
			
		||||
		Enable                 bool
 | 
			
		||||
		IconURL                string
 | 
			
		||||
		IgnoreNicks            string
 | 
			
		||||
		NickFormatter          string
 | 
			
		||||
		NicksPerRow            int
 | 
			
		||||
		PrefixMessagesWithNick bool
 | 
			
		||||
		RemoteNickFormat       string
 | 
			
		||||
		Token                  string
 | 
			
		||||
		URL                    string
 | 
			
		||||
		UseAPI                 bool
 | 
			
		||||
	}
 | 
			
		||||
	Xmpp struct {
 | 
			
		||||
		IgnoreNicks      string
 | 
			
		||||
		Jid              string
 | 
			
		||||
		Password         string
 | 
			
		||||
		Server           string
 | 
			
		||||
		Muc              string
 | 
			
		||||
		Nick             string
 | 
			
		||||
		RemoteNickFormat string
 | 
			
		||||
		Enable           bool
 | 
			
		||||
	}
 | 
			
		||||
	Channel map[string]*struct {
 | 
			
		||||
		IRC        string
 | 
			
		||||
		Mattermost string
 | 
			
		||||
		Xmpp       string
 | 
			
		||||
		Gitter     string
 | 
			
		||||
		Slack      string
 | 
			
		||||
	}
 | 
			
		||||
	General struct {
 | 
			
		||||
		GiphyAPIKey string
 | 
			
		||||
		Xmpp        bool
 | 
			
		||||
		Irc         bool
 | 
			
		||||
		Mattermost  bool
 | 
			
		||||
		Plus        bool
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewConfig(cfgfile string) *Config {
 | 
			
		||||
	var cfg Config
 | 
			
		||||
	content, err := ioutil.ReadFile(cfgfile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	err = gcfg.ReadStringInto(&cfg, string(content))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal("Failed to parse "+cfgfile+":", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &cfg
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								bridge/gitter/gitter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								bridge/gitter/gitter.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
package bgitter
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/sromku/go-gitter"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bgitter struct {
 | 
			
		||||
	c *gitter.Gitter
 | 
			
		||||
	*config.Config
 | 
			
		||||
	Remote chan config.Message
 | 
			
		||||
	Rooms  []gitter.Room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Text     string
 | 
			
		||||
	Channel  string
 | 
			
		||||
	Username string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": "gitter"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(config *config.Config, c chan config.Message) *Bgitter {
 | 
			
		||||
	b := &Bgitter{}
 | 
			
		||||
	b.Config = config
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) Connect() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	flog.Info("Trying Gitter connection")
 | 
			
		||||
	b.c = gitter.New(b.Config.Gitter.Token)
 | 
			
		||||
	_, err = b.c.GetUser()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Debugf("%#v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	flog.Info("Connection succeeded")
 | 
			
		||||
	b.setupChannels()
 | 
			
		||||
	go b.handleGitter()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) Name() string {
 | 
			
		||||
	return "gitter"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) Send(msg config.Message) error {
 | 
			
		||||
	roomID := b.getRoomID(msg.Channel)
 | 
			
		||||
	if roomID == "" {
 | 
			
		||||
		flog.Errorf("Could not find roomID for %v", msg.Channel)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	// add ZWSP because gitter echoes our own messages
 | 
			
		||||
	return b.c.SendMessage(roomID, msg.Username+msg.Text+" ")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) getRoomID(channel string) string {
 | 
			
		||||
	for _, v := range b.Rooms {
 | 
			
		||||
		if v.URI == channel {
 | 
			
		||||
			return v.ID
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) handleGitter() {
 | 
			
		||||
	for _, val := range b.Config.Channel {
 | 
			
		||||
		room := val.Gitter
 | 
			
		||||
		roomID := b.getRoomID(room)
 | 
			
		||||
		if roomID == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		stream := b.c.Stream(roomID)
 | 
			
		||||
		go b.c.Listen(stream)
 | 
			
		||||
 | 
			
		||||
		go func(stream *gitter.Stream, room string) {
 | 
			
		||||
			for {
 | 
			
		||||
				event := <-stream.Event
 | 
			
		||||
				switch ev := event.Data.(type) {
 | 
			
		||||
				case *gitter.MessageReceived:
 | 
			
		||||
					// check for ZWSP to see if it's not an echo
 | 
			
		||||
					if !strings.HasSuffix(ev.Message.Text, "") {
 | 
			
		||||
						b.Remote <- config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room, Origin: "gitter"}
 | 
			
		||||
					}
 | 
			
		||||
				case *gitter.GitterConnectionClosed:
 | 
			
		||||
					flog.Errorf("connection with gitter closed for room %s", room)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}(stream, room)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) setupChannels() {
 | 
			
		||||
	b.Rooms, _ = b.c.GetRooms()
 | 
			
		||||
	for _, val := range b.Config.Channel {
 | 
			
		||||
		flog.Infof("Joining %s as %s", val.Gitter, b.Gitter.Nick)
 | 
			
		||||
		_, err := b.c.JoinRoom(val.Gitter)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Errorf("Joining %s failed", val.Gitter)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								bridge/irc/helper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								bridge/irc/helper.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
package birc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func tableformatter(nicks []string, nicksPerRow int, continued bool) string {
 | 
			
		||||
	result := "|IRC users"
 | 
			
		||||
	if continued {
 | 
			
		||||
		result = "|(continued)"
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < 2; i++ {
 | 
			
		||||
		for j := 1; j <= nicksPerRow && j <= len(nicks); j++ {
 | 
			
		||||
			if i == 0 {
 | 
			
		||||
				result += "|"
 | 
			
		||||
			} else {
 | 
			
		||||
				result += ":-|"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		result += "\r\n|"
 | 
			
		||||
	}
 | 
			
		||||
	result += nicks[0] + "|"
 | 
			
		||||
	for i := 1; i < len(nicks); i++ {
 | 
			
		||||
		if i%nicksPerRow == 0 {
 | 
			
		||||
			result += "\r\n|" + nicks[i] + "|"
 | 
			
		||||
		} else {
 | 
			
		||||
			result += nicks[i] + "|"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func plainformatter(nicks []string, nicksPerRow int) string {
 | 
			
		||||
	return strings.Join(nicks, ", ") + " currently on IRC"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsMarkup(message string) bool {
 | 
			
		||||
	switch message[0] {
 | 
			
		||||
	case '|':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '#':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '_':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '*':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '~':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '-':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ':':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '>':
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case '=':
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								bridge/irc/irc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								bridge/irc/irc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
package birc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	ircm "github.com/sorcix/irc"
 | 
			
		||||
	"github.com/thoj/go-ircevent"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//type Bridge struct {
 | 
			
		||||
type Birc struct {
 | 
			
		||||
	i              *irc.Connection
 | 
			
		||||
	ircNick        string
 | 
			
		||||
	ircMap         map[string]string
 | 
			
		||||
	names          map[string][]string
 | 
			
		||||
	ircIgnoreNicks []string
 | 
			
		||||
	*config.Config
 | 
			
		||||
	Remote chan config.Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FancyLog struct {
 | 
			
		||||
	irc *log.Entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog FancyLog
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog.irc = log.WithFields(log.Fields{"module": "irc"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(config *config.Config, c chan config.Message) *Birc {
 | 
			
		||||
	b := &Birc{}
 | 
			
		||||
	b.Config = config
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.ircNick = b.Config.IRC.Nick
 | 
			
		||||
	b.ircMap = make(map[string]string)
 | 
			
		||||
	b.names = make(map[string][]string)
 | 
			
		||||
	b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) Command(msg *config.Message) string {
 | 
			
		||||
	switch msg.Text {
 | 
			
		||||
	case "!users":
 | 
			
		||||
		b.i.SendRaw("NAMES " + msg.Channel)
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) Connect() error {
 | 
			
		||||
	flog.irc.Info("Trying IRC connection")
 | 
			
		||||
	i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick)
 | 
			
		||||
	i.UseTLS = b.Config.IRC.UseTLS
 | 
			
		||||
	i.UseSASL = b.Config.IRC.UseSASL
 | 
			
		||||
	i.SASLLogin = b.Config.IRC.NickServNick
 | 
			
		||||
	i.SASLPassword = b.Config.IRC.NickServPassword
 | 
			
		||||
	i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify}
 | 
			
		||||
	if b.Config.IRC.Password != "" {
 | 
			
		||||
		i.Password = b.Config.IRC.Password
 | 
			
		||||
	}
 | 
			
		||||
	i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection)
 | 
			
		||||
	err := i.Connect(b.Config.IRC.Server)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	flog.irc.Info("Connection succeeded")
 | 
			
		||||
	b.i = i
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) Name() string {
 | 
			
		||||
	return "irc"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) Send(msg config.Message) error {
 | 
			
		||||
	if msg.Origin == "irc" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasPrefix(msg.Text, "!") {
 | 
			
		||||
		b.Command(&msg)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	b.i.Privmsg(msg.Channel, msg.Username+msg.Text)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) endNames(event *irc.Event) {
 | 
			
		||||
	channel := event.Arguments[1]
 | 
			
		||||
	sort.Strings(b.names[channel])
 | 
			
		||||
	maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow()
 | 
			
		||||
	continued := false
 | 
			
		||||
	for len(b.names[channel]) > maxNamesPerPost {
 | 
			
		||||
		b.Remote <- config.Message{Username: b.ircNick, Text: b.formatnicks(b.names[channel][0:maxNamesPerPost], continued), Channel: channel, Origin: "irc"}
 | 
			
		||||
		b.names[channel] = b.names[channel][maxNamesPerPost:]
 | 
			
		||||
		continued = true
 | 
			
		||||
	}
 | 
			
		||||
	b.Remote <- config.Message{Username: b.ircNick, Text: b.formatnicks(b.names[channel], continued), Channel: channel, Origin: "irc"}
 | 
			
		||||
	b.names[channel] = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) handleNewConnection(event *irc.Event) {
 | 
			
		||||
	flog.irc.Info("Registering callbacks")
 | 
			
		||||
	i := b.i
 | 
			
		||||
	b.ircNick = event.Arguments[0]
 | 
			
		||||
	i.AddCallback("PRIVMSG", b.handlePrivMsg)
 | 
			
		||||
	i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
 | 
			
		||||
	i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
 | 
			
		||||
	i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames)
 | 
			
		||||
	i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
 | 
			
		||||
	i.AddCallback(ircm.NOTICE, b.handleNotice)
 | 
			
		||||
	i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) })
 | 
			
		||||
	i.AddCallback("PING", func(e *irc.Event) {
 | 
			
		||||
		i.SendRaw("PONG :" + e.Message())
 | 
			
		||||
		flog.irc.Debugf("PING/PONG")
 | 
			
		||||
	})
 | 
			
		||||
	if b.Config.Mattermost.ShowJoinPart {
 | 
			
		||||
		i.AddCallback("JOIN", b.handleJoinPart)
 | 
			
		||||
		i.AddCallback("PART", b.handleJoinPart)
 | 
			
		||||
	}
 | 
			
		||||
	i.AddCallback("*", b.handleOther)
 | 
			
		||||
	b.setupChannels()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) handleJoinPart(event *irc.Event) {
 | 
			
		||||
	//b.Send(b.ircNick, b.ircNickFormat(event.Nick)+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) handleNotice(event *irc.Event) {
 | 
			
		||||
	if strings.Contains(event.Message(), "This nickname is registered") {
 | 
			
		||||
		b.i.Privmsg(b.Config.IRC.NickServNick, "IDENTIFY "+b.Config.IRC.NickServPassword)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) handleOther(event *irc.Event) {
 | 
			
		||||
	flog.irc.Debugf("%#v", event)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) handlePrivMsg(event *irc.Event) {
 | 
			
		||||
	flog.irc.Debugf("handlePrivMsg() %s %s", event.Nick, event.Message())
 | 
			
		||||
	msg := ""
 | 
			
		||||
	if event.Code == "CTCP_ACTION" {
 | 
			
		||||
		msg = event.Nick + " "
 | 
			
		||||
	}
 | 
			
		||||
	msg += event.Message()
 | 
			
		||||
	b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Origin: "irc"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) handleTopicWhoTime(event *irc.Event) {
 | 
			
		||||
	parts := strings.Split(event.Arguments[2], "!")
 | 
			
		||||
	t, err := strconv.ParseInt(event.Arguments[3], 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3])
 | 
			
		||||
	}
 | 
			
		||||
	user := parts[0]
 | 
			
		||||
	if len(parts) > 1 {
 | 
			
		||||
		user += " [" + parts[1] + "]"
 | 
			
		||||
	}
 | 
			
		||||
	flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) nicksPerRow() int {
 | 
			
		||||
	if b.Config.Mattermost.NicksPerRow < 1 {
 | 
			
		||||
		return 4
 | 
			
		||||
	}
 | 
			
		||||
	return b.Config.Mattermost.NicksPerRow
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) setupChannels() {
 | 
			
		||||
	for _, val := range b.Config.Channel {
 | 
			
		||||
		flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick)
 | 
			
		||||
		b.i.Join(val.IRC)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) storeNames(event *irc.Event) {
 | 
			
		||||
	channel := event.Arguments[2]
 | 
			
		||||
	b.names[channel] = append(
 | 
			
		||||
		b.names[channel],
 | 
			
		||||
		strings.Split(strings.TrimSpace(event.Message()), " ")...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Birc) formatnicks(nicks []string, continued bool) string {
 | 
			
		||||
	switch b.Config.Mattermost.NickFormatter {
 | 
			
		||||
	case "table":
 | 
			
		||||
		return tableformatter(nicks, b.nicksPerRow(), continued)
 | 
			
		||||
	default:
 | 
			
		||||
		return plainformatter(nicks, b.nicksPerRow())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										174
									
								
								bridge/mattermost/mattermost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								bridge/mattermost/mattermost.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
			
		||||
package bmattermost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterclient"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//type Bridge struct {
 | 
			
		||||
type MMhook struct {
 | 
			
		||||
	mh *matterhook.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MMapi struct {
 | 
			
		||||
	mc            *matterclient.MMClient
 | 
			
		||||
	mmMap         map[string]string
 | 
			
		||||
	mmIgnoreNicks []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MMMessage struct {
 | 
			
		||||
	Text     string
 | 
			
		||||
	Channel  string
 | 
			
		||||
	Username string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Bmattermost struct {
 | 
			
		||||
	MMhook
 | 
			
		||||
	MMapi
 | 
			
		||||
	*config.Config
 | 
			
		||||
	Plus   bool
 | 
			
		||||
	Remote chan config.Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FancyLog struct {
 | 
			
		||||
	irc  *log.Entry
 | 
			
		||||
	mm   *log.Entry
 | 
			
		||||
	xmpp *log.Entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog FancyLog
 | 
			
		||||
 | 
			
		||||
const Legacy = "legacy"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog.irc = log.WithFields(log.Fields{"module": "irc"})
 | 
			
		||||
	flog.mm = log.WithFields(log.Fields{"module": "mattermost"})
 | 
			
		||||
	flog.xmpp = log.WithFields(log.Fields{"module": "xmpp"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.Config, c chan config.Message) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{}
 | 
			
		||||
	b.Config = cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Plus = cfg.General.Plus
 | 
			
		||||
	b.mmMap = make(map[string]string)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) Command(cmd string) string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) Connect() error {
 | 
			
		||||
	if !b.Plus {
 | 
			
		||||
		b.mh = matterhook.New(b.Config.Mattermost.URL,
 | 
			
		||||
			matterhook.Config{InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify,
 | 
			
		||||
				BindAddress: b.Config.Mattermost.BindAddress})
 | 
			
		||||
	} else {
 | 
			
		||||
		b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password,
 | 
			
		||||
			b.Config.Mattermost.Team, b.Config.Mattermost.Server)
 | 
			
		||||
		b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify
 | 
			
		||||
		b.mc.NoTLS = b.Config.Mattermost.NoTLS
 | 
			
		||||
		flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server)
 | 
			
		||||
		err := b.mc.Login()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		flog.mm.Info("Login ok")
 | 
			
		||||
		b.mc.JoinChannel(b.Config.Mattermost.Channel)
 | 
			
		||||
		for _, val := range b.Config.Channel {
 | 
			
		||||
			b.mc.JoinChannel(val.Mattermost)
 | 
			
		||||
		}
 | 
			
		||||
		go b.mc.WsReceiver()
 | 
			
		||||
	}
 | 
			
		||||
	go b.handleMatter()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) Name() string {
 | 
			
		||||
	return "mattermost"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) Send(msg config.Message) error {
 | 
			
		||||
	flog.mm.Infof("mattermost send %#v", msg)
 | 
			
		||||
	if msg.Origin != "mattermost" {
 | 
			
		||||
		return b.SendType(msg.Username, msg.Text, msg.Channel, "")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) SendType(nick string, message string, channel string, mtype string) error {
 | 
			
		||||
	if b.Config.Mattermost.PrefixMessagesWithNick {
 | 
			
		||||
		/*if IsMarkup(message) {
 | 
			
		||||
			message = nick + "\n\n" + message
 | 
			
		||||
		} else {
 | 
			
		||||
		*/
 | 
			
		||||
		message = nick + " " + message
 | 
			
		||||
		//}
 | 
			
		||||
	}
 | 
			
		||||
	if !b.Plus {
 | 
			
		||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL}
 | 
			
		||||
		matterMessage.Channel = channel
 | 
			
		||||
		matterMessage.UserName = nick
 | 
			
		||||
		matterMessage.Type = mtype
 | 
			
		||||
		matterMessage.Text = message
 | 
			
		||||
		err := b.mh.Send(matterMessage)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.mm.Info(err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		flog.mm.Debug("->mattermost channel: ", channel, " ", message)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.mm.Debug("->mattermost channel plus: ", channel, " ", message)
 | 
			
		||||
	b.mc.PostMessage(b.mc.GetChannelId(channel, ""), message)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) handleMatter() {
 | 
			
		||||
	flog.mm.Infof("Choosing API based Mattermost connection: %t", b.Plus)
 | 
			
		||||
	mchan := make(chan *MMMessage)
 | 
			
		||||
	if b.Plus {
 | 
			
		||||
		go b.handleMatterClient(mchan)
 | 
			
		||||
	} else {
 | 
			
		||||
		go b.handleMatterHook(mchan)
 | 
			
		||||
	}
 | 
			
		||||
	flog.mm.Info("Start listening for Mattermost messages")
 | 
			
		||||
	for message := range mchan {
 | 
			
		||||
		texts := strings.Split(message.Text, "\n")
 | 
			
		||||
		for _, text := range texts {
 | 
			
		||||
			flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel)
 | 
			
		||||
			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Origin: "mattermost"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
 | 
			
		||||
	for message := range b.mc.MessageChan {
 | 
			
		||||
		// do not post our own messages back to irc
 | 
			
		||||
		if message.Raw.Event == "posted" && b.mc.User.Username != message.Username {
 | 
			
		||||
			flog.mm.Debugf("receiving from matterclient %#v", message)
 | 
			
		||||
			flog.mm.Debugf("receiving from matterclient %#v", message.Raw)
 | 
			
		||||
			m := &MMMessage{}
 | 
			
		||||
			m.Username = message.Username
 | 
			
		||||
			m.Channel = message.Channel
 | 
			
		||||
			m.Text = message.Text
 | 
			
		||||
			mchan <- m
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) handleMatterHook(mchan chan *MMMessage) {
 | 
			
		||||
	for {
 | 
			
		||||
		message := b.mh.Receive()
 | 
			
		||||
		flog.mm.Debugf("receiving from matterhook %#v", message)
 | 
			
		||||
		m := &MMMessage{}
 | 
			
		||||
		m.Username = message.UserName
 | 
			
		||||
		m.Text = message.Text
 | 
			
		||||
		m.Channel = message.ChannelName
 | 
			
		||||
		mchan <- m
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										180
									
								
								bridge/slack/slack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								bridge/slack/slack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
package bslack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MMMessage struct {
 | 
			
		||||
	Text     string
 | 
			
		||||
	Channel  string
 | 
			
		||||
	Username string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type bslack struct {
 | 
			
		||||
	mh *matterhook.Client
 | 
			
		||||
	sc *slack.Client
 | 
			
		||||
	//	MMapi
 | 
			
		||||
	*config.Config
 | 
			
		||||
	rtm      *slack.RTM
 | 
			
		||||
	Plus     bool
 | 
			
		||||
	Remote   chan config.Message
 | 
			
		||||
	channels []slack.Channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": "slack"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.Config, c chan config.Message) *bslack {
 | 
			
		||||
	b := &bslack{}
 | 
			
		||||
	b.Config = cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Plus = cfg.Slack.UseAPI
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) Command(cmd string) string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) Connect() error {
 | 
			
		||||
	if !b.Plus {
 | 
			
		||||
		b.mh = matterhook.New(b.Config.Slack.URL,
 | 
			
		||||
			matterhook.Config{BindAddress: b.Config.Slack.BindAddress})
 | 
			
		||||
	} else {
 | 
			
		||||
		b.sc = slack.New(b.Config.Slack.Token)
 | 
			
		||||
		flog.Infof("Trying login on slack with Token")
 | 
			
		||||
		/*
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		*/
 | 
			
		||||
		flog.Info("Login ok")
 | 
			
		||||
	}
 | 
			
		||||
	b.rtm = b.sc.NewRTM()
 | 
			
		||||
	go b.rtm.ManageConnection()
 | 
			
		||||
	go b.handleSlack()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) Name() string {
 | 
			
		||||
	return "slack"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) Send(msg config.Message) error {
 | 
			
		||||
	flog.Infof("slack send %#v", msg)
 | 
			
		||||
	if msg.Origin != "slack" {
 | 
			
		||||
		return b.SendType(msg.Username, msg.Text, msg.Channel, "")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) SendType(nick string, message string, channel string, mtype string) error {
 | 
			
		||||
	if b.Config.Slack.PrefixMessagesWithNick {
 | 
			
		||||
		message = nick + " " + message
 | 
			
		||||
	}
 | 
			
		||||
	if !b.Plus {
 | 
			
		||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.Slack.IconURL}
 | 
			
		||||
		matterMessage.Channel = channel
 | 
			
		||||
		matterMessage.UserName = nick
 | 
			
		||||
		matterMessage.Type = mtype
 | 
			
		||||
		matterMessage.Text = message
 | 
			
		||||
		err := b.mh.Send(matterMessage)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Info(err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debug("->slack channel: ", channel, " ", message)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("sent to slack channel API: %s %s", channel, message)
 | 
			
		||||
	newmsg := b.rtm.NewOutgoingMessage(message, b.getChannelByName(channel).ID)
 | 
			
		||||
	b.rtm.SendMessage(newmsg)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) getChannelByName(name string) *slack.Channel {
 | 
			
		||||
	if b.channels == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	for _, channel := range b.channels {
 | 
			
		||||
		if channel.Name == name {
 | 
			
		||||
			return &channel
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) handleSlack() {
 | 
			
		||||
	flog.Infof("Choosing API based slack connection: %t", b.Plus)
 | 
			
		||||
	mchan := make(chan *MMMessage)
 | 
			
		||||
	if b.Plus {
 | 
			
		||||
		go b.handleSlackClient(mchan)
 | 
			
		||||
	} else {
 | 
			
		||||
		go b.handleMatterHook(mchan)
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(time.Second)
 | 
			
		||||
	flog.Info("Start listening for Slack messages")
 | 
			
		||||
	for message := range mchan {
 | 
			
		||||
		texts := strings.Split(message.Text, "\n")
 | 
			
		||||
		for _, text := range texts {
 | 
			
		||||
			flog.Debug("Sending message from " + message.Username + " to " + message.Channel)
 | 
			
		||||
			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Origin: "slack"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
	for msg := range b.rtm.IncomingEvents {
 | 
			
		||||
		switch ev := msg.Data.(type) {
 | 
			
		||||
		case *slack.MessageEvent:
 | 
			
		||||
			flog.Debugf("%#v", ev)
 | 
			
		||||
			channel, err := b.rtm.GetChannelInfo(ev.Channel)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			user, err := b.rtm.GetUserInfo(ev.User)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			m := &MMMessage{}
 | 
			
		||||
			m.Username = user.Name
 | 
			
		||||
			m.Channel = channel.Name
 | 
			
		||||
			m.Text = ev.Text
 | 
			
		||||
			mchan <- m
 | 
			
		||||
		case *slack.OutgoingErrorEvent:
 | 
			
		||||
			flog.Debugf("%#v", ev.Error())
 | 
			
		||||
		case *slack.ConnectedEvent:
 | 
			
		||||
			b.channels = ev.Info.Channels
 | 
			
		||||
			for _, val := range b.Config.Channel {
 | 
			
		||||
				channel := b.getChannelByName(val.Slack)
 | 
			
		||||
				if channel != nil && !channel.IsMember {
 | 
			
		||||
					flog.Infof("Joining %s", val.Slack)
 | 
			
		||||
					b.sc.JoinChannel(channel.ID)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case *slack.InvalidAuthEvent:
 | 
			
		||||
			flog.Fatalf("Invalid Token %#v", ev)
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bslack) handleMatterHook(mchan chan *MMMessage) {
 | 
			
		||||
	for {
 | 
			
		||||
		message := b.mh.Receive()
 | 
			
		||||
		flog.Debugf("receiving from slack %#v", message)
 | 
			
		||||
		m := &MMMessage{}
 | 
			
		||||
		m.Username = message.UserName
 | 
			
		||||
		m.Text = message.Text
 | 
			
		||||
		m.Channel = message.ChannelName
 | 
			
		||||
		mchan <- m
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								bridge/xmpp/xmpp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								bridge/xmpp/xmpp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
package bxmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/mattn/go-xmpp"
 | 
			
		||||
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bxmpp struct {
 | 
			
		||||
	xc      *xmpp.Client
 | 
			
		||||
	xmppMap map[string]string
 | 
			
		||||
	*config.Config
 | 
			
		||||
	Remote chan config.Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FancyLog struct {
 | 
			
		||||
	xmpp *log.Entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Text     string
 | 
			
		||||
	Channel  string
 | 
			
		||||
	Username string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog FancyLog
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog.xmpp = log.WithFields(log.Fields{"module": "xmpp"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(config *config.Config, c chan config.Message) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{}
 | 
			
		||||
	b.xmppMap = make(map[string]string)
 | 
			
		||||
	b.Config = config
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) Connect() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	flog.xmpp.Info("Trying XMPP connection")
 | 
			
		||||
	b.xc, err = b.createXMPP()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.xmpp.Debugf("%#v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	flog.xmpp.Info("Connection succeeded")
 | 
			
		||||
	b.setupChannels()
 | 
			
		||||
	go b.handleXmpp()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) Name() string {
 | 
			
		||||
	return "xmpp"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) Send(msg config.Message) error {
 | 
			
		||||
	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Xmpp.Muc, Text: msg.Username + msg.Text})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
 | 
			
		||||
	options := xmpp.Options{
 | 
			
		||||
		Host:     b.Config.Xmpp.Server,
 | 
			
		||||
		User:     b.Config.Xmpp.Jid,
 | 
			
		||||
		Password: b.Config.Xmpp.Password,
 | 
			
		||||
		NoTLS:    true,
 | 
			
		||||
		StartTLS: true,
 | 
			
		||||
		//StartTLS:      false,
 | 
			
		||||
		Debug:                        true,
 | 
			
		||||
		Session:                      true,
 | 
			
		||||
		Status:                       "",
 | 
			
		||||
		StatusMessage:                "",
 | 
			
		||||
		Resource:                     "",
 | 
			
		||||
		InsecureAllowUnencryptedAuth: false,
 | 
			
		||||
		//InsecureAllowUnencryptedAuth: true,
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	b.xc, err = options.NewClient()
 | 
			
		||||
	return b.xc, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) setupChannels() {
 | 
			
		||||
	for _, val := range b.Config.Channel {
 | 
			
		||||
		flog.xmpp.Infof("Joining %s as %s", val.Xmpp, b.Xmpp.Nick)
 | 
			
		||||
		b.xc.JoinMUCNoHistory(val.Xmpp+"@"+b.Xmpp.Muc, b.Xmpp.Nick)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) xmppKeepAlive() {
 | 
			
		||||
	go func() {
 | 
			
		||||
		ticker := time.NewTicker(90 * time.Second)
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-ticker.C:
 | 
			
		||||
				b.xc.Send(xmpp.Chat{})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) handleXmpp() error {
 | 
			
		||||
	for {
 | 
			
		||||
		m, err := b.xc.Recv()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		switch v := m.(type) {
 | 
			
		||||
		case xmpp.Chat:
 | 
			
		||||
			var channel, nick string
 | 
			
		||||
			if v.Type == "groupchat" {
 | 
			
		||||
				s := strings.Split(v.Remote, "@")
 | 
			
		||||
				if len(s) == 2 {
 | 
			
		||||
					channel = s[0]
 | 
			
		||||
				}
 | 
			
		||||
				s = strings.Split(s[1], "/")
 | 
			
		||||
				if len(s) == 2 {
 | 
			
		||||
					nick = s[1]
 | 
			
		||||
				}
 | 
			
		||||
				if nick != b.Xmpp.Nick {
 | 
			
		||||
					flog.xmpp.Infof("sending message to remote %s %s %s", nick, v.Text, channel)
 | 
			
		||||
					b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Origin: "xmpp"}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case xmpp.Presence:
 | 
			
		||||
			// do nothing
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								changelog.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
# v0.6.1
 | 
			
		||||
## New features
 | 
			
		||||
* Slack support added.  See matterbridge.conf.sample for more information
 | 
			
		||||
## Bugfix
 | 
			
		||||
* Fix 100% CPU bug on incorrect closed connections
 | 
			
		||||
 | 
			
		||||
# v0.6.0-beta2
 | 
			
		||||
## New features
 | 
			
		||||
* Gitter support added.  See matterbridge.conf.sample for more information
 | 
			
		||||
 | 
			
		||||
# v0.6.0-beta1
 | 
			
		||||
## Breaking changes from 0.5 to 0.6
 | 
			
		||||
### commandline
 | 
			
		||||
* -plus switch deprecated. Use ```Plus=true``` or ```Plus``` in ```[general]``` section
 | 
			
		||||
 | 
			
		||||
### IRC section
 | 
			
		||||
* ```Enabled``` added (default false)  
 | 
			
		||||
Add ```Enabled=true``` or ```Enabled``` to the ```[IRC]``` section if you want to enable the IRC bridge
 | 
			
		||||
 | 
			
		||||
### Mattermost section
 | 
			
		||||
* ```Enabled``` added (default false)  
 | 
			
		||||
Add ```Enabled=true``` or ```Enabled``` to the ```[mattermost]``` section if you want to enable the mattermost bridge
 | 
			
		||||
 | 
			
		||||
### General section
 | 
			
		||||
* Use ```Plus=true``` or ```Plus``` in ```[general]``` section to enable the API version of matterbridge
 | 
			
		||||
 | 
			
		||||
## New features
 | 
			
		||||
* Matterbridge now bridges between any specified protocol (not only mattermost anymore) 
 | 
			
		||||
* XMPP support added.  See matterbridge.conf.sample for more information
 | 
			
		||||
* RemoteNickFormat {BRIDGE} variable added  
 | 
			
		||||
You can now add the originating bridge to ```RemoteNickFormat```  
 | 
			
		||||
eg ```RemoteNickFormat="[{BRIDGE}] <{NICK}> "```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# v0.5.0
 | 
			
		||||
## Breaking changes from 0.4 to 0.5 for matterbridge (webhooks version)
 | 
			
		||||
### IRC section
 | 
			
		||||
#### Server
 | 
			
		||||
Port removed, added to server
 | 
			
		||||
```
 | 
			
		||||
server="irc.freenode.net"
 | 
			
		||||
port=6667
 | 
			
		||||
```
 | 
			
		||||
changed to
 | 
			
		||||
```
 | 
			
		||||
server="irc.freenode.net:6667"
 | 
			
		||||
```
 | 
			
		||||
#### Channel
 | 
			
		||||
Removed see Channels section below
 | 
			
		||||
 | 
			
		||||
#### UseSlackCircumfix=true
 | 
			
		||||
Removed, can be done by using ```RemoteNickFormat="<{NICK}> "```
 | 
			
		||||
 | 
			
		||||
### Mattermost section
 | 
			
		||||
#### BindAddress
 | 
			
		||||
Port removed, added to BindAddress
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
BindAddress="0.0.0.0"
 | 
			
		||||
port=9999
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
changed to
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
BindAddress="0.0.0.0:9999"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Token
 | 
			
		||||
Removed
 | 
			
		||||
 | 
			
		||||
### Channels section
 | 
			
		||||
```
 | 
			
		||||
[Token "outgoingwebhooktoken1"] 
 | 
			
		||||
IRCChannel="#off-topic"
 | 
			
		||||
MMChannel="off-topic"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
changed to
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[Channel "channelnameofchoice"] 
 | 
			
		||||
IRC="#off-topic"
 | 
			
		||||
Mattermost="off-topic"
 | 
			
		||||
```
 | 
			
		||||
@@ -1,37 +1,287 @@
 | 
			
		||||
#This is configuration for matterbridge.
 | 
			
		||||
###################################################################
 | 
			
		||||
#IRC section
 | 
			
		||||
###################################################################
 | 
			
		||||
[IRC]
 | 
			
		||||
server="irc.freenode.net"
 | 
			
		||||
port=6667
 | 
			
		||||
#Enable enables this bridge
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
Enable=true
 | 
			
		||||
#irc server to connect to. 
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Server="irc.freenode.net:6667"
 | 
			
		||||
 | 
			
		||||
#Enable to use TLS connection to your irc server. 
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
UseTLS=false
 | 
			
		||||
 | 
			
		||||
#Enable SASL (PLAIN) authentication. (freenode requires this from eg AWS hosts)
 | 
			
		||||
#It uses NickServNick and NickServPassword as login and password
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
UseSASL=false
 | 
			
		||||
 | 
			
		||||
#Enable to not verify the certificate on your irc server. i
 | 
			
		||||
#e.g. when using selfsigned certificates
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
SkipTLSVerify=true
 | 
			
		||||
nick="matterbot"
 | 
			
		||||
channel="#matterbridge"
 | 
			
		||||
UseSlackCircumfix=false
 | 
			
		||||
 | 
			
		||||
#Your nick on irc. 
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Nick="matterbot"
 | 
			
		||||
 | 
			
		||||
#If you registered your bot with a service like Nickserv on freenode. 
 | 
			
		||||
#Also being used when UseSASL=true
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
NickServNick="nickserv"
 | 
			
		||||
NickServPassword="secret"
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
#OPTIONAL (default {BRIDGE}-{NICK})
 | 
			
		||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> 
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore. 
 | 
			
		||||
#Messages from those users will not be sent to other bridges.
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#XMPP section
 | 
			
		||||
###################################################################
 | 
			
		||||
[XMPP]
 | 
			
		||||
#Enable enables this bridge
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
Enable=true
 | 
			
		||||
 | 
			
		||||
#xmpp server to connect to. 
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Server="jabber.example.com:5222"
 | 
			
		||||
 | 
			
		||||
#Jid
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Jid="user@example.com"
 | 
			
		||||
 | 
			
		||||
#Password
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Password="yourpass"
 | 
			
		||||
 | 
			
		||||
#MUC
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Muc="conference.jabber.example.com"
 | 
			
		||||
 | 
			
		||||
#Your nick in the rooms
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Nick="xmppbot"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#mattermost section
 | 
			
		||||
###################################################################
 | 
			
		||||
 | 
			
		||||
[mattermost]
 | 
			
		||||
url="http://yourdomain/hooks/yourhookkey"
 | 
			
		||||
port=9999
 | 
			
		||||
showjoinpart=true
 | 
			
		||||
#remove token when using multiple channels!
 | 
			
		||||
token=yourtokenfrommattermost
 | 
			
		||||
#Enable enables this bridge
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
Enable=true
 | 
			
		||||
 | 
			
		||||
#### Settings for webhook matterbridge.
 | 
			
		||||
#### These settings will not be used when using -plus switch which doesn't use 
 | 
			
		||||
#### webhooks.
 | 
			
		||||
 | 
			
		||||
#Url is your incoming webhook url as specified in mattermost. 
 | 
			
		||||
#See account settings - integrations - incoming webhooks on mattermost.
 | 
			
		||||
#REQUIRED
 | 
			
		||||
URL="https://yourdomain/hooks/yourhookkey"
 | 
			
		||||
 | 
			
		||||
#Address to listen on for outgoing webhook requests from mattermost.
 | 
			
		||||
#See account settings - integrations - outgoing webhooks on mattermost.
 | 
			
		||||
#This setting will not be used when using -plus switch which doesn't use 
 | 
			
		||||
#webhooks
 | 
			
		||||
#REQUIRED
 | 
			
		||||
BindAddress="0.0.0.0:9999"
 | 
			
		||||
 | 
			
		||||
#Icon that will be showed in mattermost. 
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
IconURL="http://youricon.png"
 | 
			
		||||
#SkipTLSVerify=true
 | 
			
		||||
#BindAddress="0.0.0.0"
 | 
			
		||||
 | 
			
		||||
#### Settings for matterbridge -plus
 | 
			
		||||
#### Thse settings will only be used when using the -plus switch.
 | 
			
		||||
 | 
			
		||||
#The mattermost hostname. 
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Server="yourmattermostserver.domain"
 | 
			
		||||
 | 
			
		||||
#Your team on mattermost. 
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Team="yourteam"
 | 
			
		||||
 | 
			
		||||
#login/pass of your bot. 
 | 
			
		||||
#Use a dedicated user for this and not your own! 
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Login="yourlogin"
 | 
			
		||||
Password="yourpass"
 | 
			
		||||
 | 
			
		||||
#Enable this to make a http connection (instead of https) to your mattermost. 
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
NoTLS=false
 | 
			
		||||
 | 
			
		||||
#### Shared settings for matterbridge and -plus
 | 
			
		||||
 | 
			
		||||
#Enable to not verify the certificate on your mattermost server. 
 | 
			
		||||
#e.g. when using selfsigned certificates
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
SkipTLSVerify=true
 | 
			
		||||
 | 
			
		||||
#Enable to show IRC joins/parts in mattermost. 
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
#Whether to prefix messages from other bridges to mattermost with the sender's nick. 
 | 
			
		||||
#Useful if username overrides for incoming webhooks isn't enabled on the 
 | 
			
		||||
#mattermost server. If you set PrefixMessagesWithNick to true, each message 
 | 
			
		||||
#from bridge to Mattermost will by default be prefixed by "bridge-" + nick. You can, 
 | 
			
		||||
#however, modify how the messages appear, by setting (and modifying) RemoteNickFormat 
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
PrefixMessagesWithNick=false
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
#OPTIONAL (default {BRIDGE}-{NICK})
 | 
			
		||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> 
 | 
			
		||||
 | 
			
		||||
#how to format the list of IRC nicks when displayed in mattermost. 
 | 
			
		||||
#Possible options are "table" and "plain"
 | 
			
		||||
#OPTIONAL (default plain)
 | 
			
		||||
NickFormatter=plain
 | 
			
		||||
#How many nicks to list per row for formatters that support this. 
 | 
			
		||||
#OPTIONAL (default 4)
 | 
			
		||||
NicksPerRow=4
 | 
			
		||||
#NickServNick="nickserv"
 | 
			
		||||
#NickServPassword="secret"
 | 
			
		||||
 | 
			
		||||
[general]
 | 
			
		||||
GiphyAPIKey=dc6zaTOxFJmzC
 | 
			
		||||
#Nicks you want to ignore. Messages from those users will not be bridged.
 | 
			
		||||
#OPTIONAL 
 | 
			
		||||
IgnoreNicks="mmbot spammer2"
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#Gitter section
 | 
			
		||||
#Best to make a dedicated gitter account for the bot.
 | 
			
		||||
###################################################################
 | 
			
		||||
[Gitter]
 | 
			
		||||
#Enable enables this bridge
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
Enable=true
 | 
			
		||||
 | 
			
		||||
#Token to connect with Gitter API
 | 
			
		||||
#You can get your token by going to https://developer.gitter.im/docs/welcome and SIGN IN
 | 
			
		||||
#REQUIRED
 | 
			
		||||
Token="Yourtokenhere"
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore. Messages of those users will not be bridged.
 | 
			
		||||
#OPTIONAL 
 | 
			
		||||
IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
#OPTIONAL (default {BRIDGE}-{NICK})
 | 
			
		||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> 
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#slack section
 | 
			
		||||
###################################################################
 | 
			
		||||
 | 
			
		||||
[slack]
 | 
			
		||||
#Enable enables this bridge
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
Enable=true
 | 
			
		||||
 | 
			
		||||
#### Settings for webhook matterbridge.
 | 
			
		||||
#### These settings will not be used when useAPI is enabled
 | 
			
		||||
 | 
			
		||||
#Url is your incoming webhook url as specified in slack
 | 
			
		||||
#See account settings - integrations - incoming webhooks on slack
 | 
			
		||||
#REQUIRED (unless useAPI=true)
 | 
			
		||||
URL="https://hooks.slack.com/services/yourhook"
 | 
			
		||||
 | 
			
		||||
#Address to listen on for outgoing webhook requests from slack
 | 
			
		||||
#See account settings - integrations - outgoing webhooks on slack
 | 
			
		||||
#This setting will not be used when useAPI is eanbled
 | 
			
		||||
#webhooks
 | 
			
		||||
#REQUIRED (unless useAPI=true)
 | 
			
		||||
BindAddress="0.0.0.0:9999"
 | 
			
		||||
 | 
			
		||||
#Icon that will be showed in slack
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
IconURL="http://youricon.png"
 | 
			
		||||
 | 
			
		||||
#### Settings for using slack API
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
useAPI=false
 | 
			
		||||
 | 
			
		||||
#Token to connect with the Slack API
 | 
			
		||||
#REQUIRED (when useAPI=true)
 | 
			
		||||
Token="yourslacktoken"
 | 
			
		||||
 | 
			
		||||
#### Shared settings for webhooks and API
 | 
			
		||||
 | 
			
		||||
#Whether to prefix messages from other bridges to mattermost with the sender's nick. 
 | 
			
		||||
#Useful if username overrides for incoming webhooks isn't enabled on the 
 | 
			
		||||
#slack server. If you set PrefixMessagesWithNick to true, each message 
 | 
			
		||||
#from bridge to Slack will by default be prefixed by "bridge-" + nick. You can, 
 | 
			
		||||
#however, modify how the messages appear, by setting (and modifying) RemoteNickFormat 
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
PrefixMessagesWithNick=false
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
#OPTIONAL (default {BRIDGE}-{NICK})
 | 
			
		||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> 
 | 
			
		||||
 | 
			
		||||
#how to format the list of IRC nicks when displayed in slack
 | 
			
		||||
#Possible options are "table" and "plain"
 | 
			
		||||
#OPTIONAL (default plain)
 | 
			
		||||
NickFormatter=plain
 | 
			
		||||
#How many nicks to list per row for formatters that support this. 
 | 
			
		||||
#OPTIONAL (default 4)
 | 
			
		||||
NicksPerRow=4
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore. Messages from those users will not be bridged.
 | 
			
		||||
#OPTIONAL 
 | 
			
		||||
IgnoreNicks="mmbot spammer2"
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#multiple channel config
 | 
			
		||||
#token you can find in your outgoing webhook
 | 
			
		||||
[Token "outgoingwebhooktoken1"] 
 | 
			
		||||
IRCChannel="#off-topic"
 | 
			
		||||
MMChannel="off-topic"
 | 
			
		||||
###################################################################
 | 
			
		||||
#You can specify multiple channels. 
 | 
			
		||||
#The name is just an identifier for you.
 | 
			
		||||
#REQUIRED (at least 1 channel)
 | 
			
		||||
[Channel "channel1"] 
 | 
			
		||||
#Choose the IRC channel to send messages to.
 | 
			
		||||
IRC="#off-topic"
 | 
			
		||||
#Choose the mattermost channel to messages to.
 | 
			
		||||
mattermost="off-topic"
 | 
			
		||||
#Choose the xmpp channel to send messages to.
 | 
			
		||||
xmpp="off-topic"
 | 
			
		||||
#Choose the Gitter channel to send messages to.
 | 
			
		||||
#Gitter channels are named "user/repo"
 | 
			
		||||
gitter="42wim/matterbridge"
 | 
			
		||||
#Choose the slack channel to send messages to.
 | 
			
		||||
slack="general"
 | 
			
		||||
 | 
			
		||||
[Token "outgoingwebhooktoken2"]
 | 
			
		||||
IRCChannel="#testing"
 | 
			
		||||
MMChannel="testing"
 | 
			
		||||
[Channel "testchannel"]
 | 
			
		||||
IRC="#testing"
 | 
			
		||||
mattermost="testing"
 | 
			
		||||
xmpp="testing"
 | 
			
		||||
gitter="user/repo"
 | 
			
		||||
slack="testing"
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#general
 | 
			
		||||
###################################################################
 | 
			
		||||
[general]
 | 
			
		||||
#request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key. 
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
GiphyApiKey="dc6zaTOxFJmzC"
 | 
			
		||||
 | 
			
		||||
#Enabling plus means you'll use the API version instead of the webhooks one
 | 
			
		||||
Plus=false
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,14 @@ package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"github.com/42wim/matterbridge-plus/bridge"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var version = "0.6.1"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
 | 
			
		||||
}
 | 
			
		||||
@@ -13,11 +17,25 @@ func init() {
 | 
			
		||||
func main() {
 | 
			
		||||
	flagConfig := flag.String("conf", "matterbridge.conf", "config file")
 | 
			
		||||
	flagDebug := flag.Bool("debug", false, "enable debug")
 | 
			
		||||
	flagVersion := flag.Bool("version", false, "show version")
 | 
			
		||||
	flagPlus := flag.Bool("plus", false, "running using API instead of webhooks (deprecated, set Plus flag in [general] config)")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	if *flagVersion {
 | 
			
		||||
		fmt.Println("version:", version)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	if *flagDebug {
 | 
			
		||||
		log.Info("enabling debug")
 | 
			
		||||
		log.SetLevel(log.DebugLevel)
 | 
			
		||||
	}
 | 
			
		||||
	bridge.NewBridge("matterbot", bridge.NewConfig(*flagConfig), "legacy")
 | 
			
		||||
	select {}
 | 
			
		||||
	fmt.Println("running version", version)
 | 
			
		||||
	cfg := config.NewConfig(*flagConfig)
 | 
			
		||||
	if *flagPlus {
 | 
			
		||||
		cfg.General.Plus = true
 | 
			
		||||
	}
 | 
			
		||||
	err := bridge.NewBridge(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Debugf("starting bridge failed %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										644
									
								
								matterclient/matterclient.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										644
									
								
								matterclient/matterclient.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,644 @@
 | 
			
		||||
package matterclient
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/cookiejar"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/jpillora/backoff"
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Credentials struct {
 | 
			
		||||
	Login         string
 | 
			
		||||
	Team          string
 | 
			
		||||
	Pass          string
 | 
			
		||||
	Server        string
 | 
			
		||||
	NoTLS         bool
 | 
			
		||||
	SkipTLSVerify bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Raw      *model.WebSocketEvent
 | 
			
		||||
	Post     *model.Post
 | 
			
		||||
	Team     string
 | 
			
		||||
	Channel  string
 | 
			
		||||
	Username string
 | 
			
		||||
	Text     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Team struct {
 | 
			
		||||
	Team         *model.Team
 | 
			
		||||
	Id           string
 | 
			
		||||
	Channels     *model.ChannelList
 | 
			
		||||
	MoreChannels *model.ChannelList
 | 
			
		||||
	Users        map[string]*model.User
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MMClient struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*Credentials
 | 
			
		||||
	Team        *Team
 | 
			
		||||
	OtherTeams  []*Team
 | 
			
		||||
	Client      *model.Client
 | 
			
		||||
	User        *model.User
 | 
			
		||||
	Users       map[string]*model.User
 | 
			
		||||
	MessageChan chan *Message
 | 
			
		||||
	log         *log.Entry
 | 
			
		||||
	WsClient    *websocket.Conn
 | 
			
		||||
	WsQuit      bool
 | 
			
		||||
	WsAway      bool
 | 
			
		||||
	WsConnected bool
 | 
			
		||||
	WsSequence  int64
 | 
			
		||||
	WsPingChan  chan *model.WebSocketResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(login, pass, team, server string) *MMClient {
 | 
			
		||||
	cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
 | 
			
		||||
	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)}
 | 
			
		||||
	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"})
 | 
			
		||||
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
 | 
			
		||||
	return mmclient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) SetLogLevel(level string) {
 | 
			
		||||
	l, err := log.ParseLevel(level)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.SetLevel(log.InfoLevel)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.SetLevel(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) Login() error {
 | 
			
		||||
	m.WsConnected = false
 | 
			
		||||
	if m.WsQuit {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	b := &backoff.Backoff{
 | 
			
		||||
		Min:    time.Second,
 | 
			
		||||
		Max:    5 * time.Minute,
 | 
			
		||||
		Jitter: true,
 | 
			
		||||
	}
 | 
			
		||||
	uriScheme := "https://"
 | 
			
		||||
	wsScheme := "wss://"
 | 
			
		||||
	if m.NoTLS {
 | 
			
		||||
		uriScheme = "http://"
 | 
			
		||||
		wsScheme = "ws://"
 | 
			
		||||
	}
 | 
			
		||||
	// login to mattermost
 | 
			
		||||
	m.Client = model.NewClient(uriScheme + m.Credentials.Server)
 | 
			
		||||
	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
 | 
			
		||||
	var myinfo *model.Result
 | 
			
		||||
	var appErr *model.AppError
 | 
			
		||||
	var logmsg = "trying login"
 | 
			
		||||
	for {
 | 
			
		||||
		m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
 | 
			
		||||
		if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
 | 
			
		||||
			m.log.Debugf(logmsg+" with %s", model.SESSION_COOKIE_TOKEN)
 | 
			
		||||
			token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=")
 | 
			
		||||
			if len(token) != 2 {
 | 
			
		||||
				return errors.New("incorrect MMAUTHTOKEN. valid input is MMAUTHTOKEN=yourtoken")
 | 
			
		||||
			}
 | 
			
		||||
			m.Client.HttpClient.Jar = m.createCookieJar(token[1])
 | 
			
		||||
			m.Client.MockSession(token[1])
 | 
			
		||||
			myinfo, appErr = m.Client.GetMe("")
 | 
			
		||||
			if appErr != nil {
 | 
			
		||||
				return errors.New(appErr.DetailedError)
 | 
			
		||||
			}
 | 
			
		||||
			if myinfo.Data.(*model.User) == nil {
 | 
			
		||||
				m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
 | 
			
		||||
				return errors.New("invalid " + model.SESSION_COOKIE_TOKEN)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
 | 
			
		||||
		}
 | 
			
		||||
		if appErr != nil {
 | 
			
		||||
			d := b.Duration()
 | 
			
		||||
			m.log.Debug(appErr.DetailedError)
 | 
			
		||||
			if !strings.Contains(appErr.DetailedError, "connection refused") &&
 | 
			
		||||
				!strings.Contains(appErr.DetailedError, "invalid character") {
 | 
			
		||||
				if appErr.Message == "" {
 | 
			
		||||
					return errors.New(appErr.DetailedError)
 | 
			
		||||
				}
 | 
			
		||||
				return errors.New(appErr.Message)
 | 
			
		||||
			}
 | 
			
		||||
			m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
 | 
			
		||||
			time.Sleep(d)
 | 
			
		||||
			logmsg = "retrying login"
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
	// reset timer
 | 
			
		||||
	b.Reset()
 | 
			
		||||
 | 
			
		||||
	err := m.initUser()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if m.Team == nil {
 | 
			
		||||
		return errors.New("team not found")
 | 
			
		||||
	}
 | 
			
		||||
	// set our team id as default route
 | 
			
		||||
	m.Client.SetTeamId(m.Team.Id)
 | 
			
		||||
 | 
			
		||||
	// setup websocket connection
 | 
			
		||||
	wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX + "/users/websocket"
 | 
			
		||||
	header := http.Header{}
 | 
			
		||||
	header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
 | 
			
		||||
 | 
			
		||||
	m.log.Debug("WsClient: making connection")
 | 
			
		||||
	for {
 | 
			
		||||
		wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
 | 
			
		||||
		m.WsClient, _, err = wsDialer.Dial(wsurl, header)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			d := b.Duration()
 | 
			
		||||
			m.log.Debugf("WSS: %s, reconnecting in %s", err, d)
 | 
			
		||||
			time.Sleep(d)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
	b.Reset()
 | 
			
		||||
 | 
			
		||||
	m.WsSequence = 1
 | 
			
		||||
	m.WsPingChan = make(chan *model.WebSocketResponse)
 | 
			
		||||
	// only start to parse WS messages when login is completely done
 | 
			
		||||
	m.WsConnected = true
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) Logout() error {
 | 
			
		||||
	m.log.Debugf("logout as %s (team: %s) on %s", m.Credentials.Login, m.Credentials.Team, m.Credentials.Server)
 | 
			
		||||
	m.WsQuit = true
 | 
			
		||||
	m.WsClient.Close()
 | 
			
		||||
	m.WsClient.UnderlyingConn().Close()
 | 
			
		||||
	_, err := m.Client.Logout()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) WsReceiver() {
 | 
			
		||||
	for {
 | 
			
		||||
		var rawMsg json.RawMessage
 | 
			
		||||
		var err error
 | 
			
		||||
 | 
			
		||||
		if m.WsQuit {
 | 
			
		||||
			m.log.Debug("exiting WsReceiver")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !m.WsConnected {
 | 
			
		||||
			time.Sleep(time.Millisecond * 100)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, rawMsg, err = m.WsClient.ReadMessage(); err != nil {
 | 
			
		||||
			m.log.Error("error:", err)
 | 
			
		||||
			// reconnect
 | 
			
		||||
			m.Login()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var event model.WebSocketEvent
 | 
			
		||||
		if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() {
 | 
			
		||||
			m.log.Debugf("WsReceiver: %#v", event)
 | 
			
		||||
			msg := &Message{Raw: &event, Team: m.Credentials.Team}
 | 
			
		||||
			m.parseMessage(msg)
 | 
			
		||||
			m.MessageChan <- msg
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var response model.WebSocketResponse
 | 
			
		||||
		if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() {
 | 
			
		||||
			m.log.Debugf("WsReceiver: %#v", response)
 | 
			
		||||
			m.parseResponse(response)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) parseMessage(rmsg *Message) {
 | 
			
		||||
	switch rmsg.Raw.Event {
 | 
			
		||||
	case model.WEBSOCKET_EVENT_POSTED:
 | 
			
		||||
		m.parseActionPost(rmsg)
 | 
			
		||||
		/*
 | 
			
		||||
			case model.ACTION_USER_REMOVED:
 | 
			
		||||
				m.handleWsActionUserRemoved(&rmsg)
 | 
			
		||||
			case model.ACTION_USER_ADDED:
 | 
			
		||||
				m.handleWsActionUserAdded(&rmsg)
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) parseResponse(rmsg model.WebSocketResponse) {
 | 
			
		||||
	if rmsg.Data != nil {
 | 
			
		||||
		// ping reply
 | 
			
		||||
		if rmsg.Data["text"].(string) == "pong" {
 | 
			
		||||
			m.WsPingChan <- &rmsg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) parseActionPost(rmsg *Message) {
 | 
			
		||||
	data := model.PostFromJson(strings.NewReader(rmsg.Raw.Data["post"].(string)))
 | 
			
		||||
	// we don't have the user, refresh the userlist
 | 
			
		||||
	if m.GetUser(data.UserId) == nil {
 | 
			
		||||
		m.UpdateUsers()
 | 
			
		||||
	}
 | 
			
		||||
	rmsg.Username = m.GetUser(data.UserId).Username
 | 
			
		||||
	rmsg.Channel = m.GetChannelName(data.ChannelId)
 | 
			
		||||
	rmsg.Team = m.GetTeamName(rmsg.Raw.TeamId)
 | 
			
		||||
	// direct message
 | 
			
		||||
	if rmsg.Raw.Data["channel_type"] == "D" {
 | 
			
		||||
		rmsg.Channel = m.GetUser(data.UserId).Username
 | 
			
		||||
	}
 | 
			
		||||
	rmsg.Text = data.Message
 | 
			
		||||
	rmsg.Post = data
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateUsers() error {
 | 
			
		||||
	mmusers, err := m.Client.GetProfilesForDirectMessageList(m.Team.Id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New(err.DetailedError)
 | 
			
		||||
	}
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	m.Users = mmusers.Data.(map[string]*model.User)
 | 
			
		||||
	m.Unlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateChannels() error {
 | 
			
		||||
	mmchannels, err := m.Client.GetChannels("")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New(err.DetailedError)
 | 
			
		||||
	}
 | 
			
		||||
	mmchannels2, err := m.Client.GetMoreChannels("")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New(err.DetailedError)
 | 
			
		||||
	}
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	m.Team.Channels = mmchannels.Data.(*model.ChannelList)
 | 
			
		||||
	m.Team.MoreChannels = mmchannels2.Data.(*model.ChannelList)
 | 
			
		||||
	m.Unlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetChannelName(channelId string) string {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
 | 
			
		||||
			if channel.Id == channelId {
 | 
			
		||||
				return channel.Name
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetChannelId(name string, teamId string) string {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	if teamId == "" {
 | 
			
		||||
		teamId = m.Team.Id
 | 
			
		||||
	}
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		if t.Id == teamId {
 | 
			
		||||
			for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
 | 
			
		||||
				if channel.Name == name {
 | 
			
		||||
					return channel.Id
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetChannelHeader(channelId string) string {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
 | 
			
		||||
			if channel.Id == channelId {
 | 
			
		||||
				return channel.Header
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) PostMessage(channelId string, text string) {
 | 
			
		||||
	post := &model.Post{ChannelId: channelId, Message: text}
 | 
			
		||||
	m.Client.CreatePost(post)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) JoinChannel(channelId string) error {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	for _, c := range m.Team.Channels.Channels {
 | 
			
		||||
		if c.Id == channelId {
 | 
			
		||||
			m.log.Debug("Not joining ", channelId, " already joined.")
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	m.log.Debug("Joining ", channelId)
 | 
			
		||||
	_, err := m.Client.JoinChannel(channelId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("failed to join")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList {
 | 
			
		||||
	res, err := m.Client.GetPostsSince(channelId, time)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return res.Data.(*model.PostList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) SearchPosts(query string) *model.PostList {
 | 
			
		||||
	res, err := m.Client.SearchPosts(query, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return res.Data.(*model.PostList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList {
 | 
			
		||||
	res, err := m.Client.GetPosts(channelId, 0, limit, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return res.Data.(*model.PostList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetPublicLink(filename string) string {
 | 
			
		||||
	res, err := m.Client.GetPublicLink(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return res.Data.(string)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetPublicLinks(filenames []string) []string {
 | 
			
		||||
	var output []string
 | 
			
		||||
	for _, f := range filenames {
 | 
			
		||||
		res, err := m.Client.GetPublicLink(f)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		output = append(output, res.Data.(string))
 | 
			
		||||
	}
 | 
			
		||||
	return output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
 | 
			
		||||
	data := make(map[string]string)
 | 
			
		||||
	data["channel_id"] = channelId
 | 
			
		||||
	data["channel_header"] = header
 | 
			
		||||
	m.log.Debugf("updating channelheader %#v, %#v", channelId, header)
 | 
			
		||||
	_, err := m.Client.UpdateChannelHeader(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
			
		||||
	m.log.Debugf("posting lastview %#v", channelId)
 | 
			
		||||
	_, err := m.Client.UpdateLastViewedAt(channelId, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		m.log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UsernamesInChannel(channelId string) []string {
 | 
			
		||||
	ceiRes, err := m.Client.GetChannelExtraInfo(channelId, 5000, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelId, err)
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
	extra := ceiRes.Data.(*model.ChannelExtra)
 | 
			
		||||
	result := []string{}
 | 
			
		||||
	for _, member := range extra.Members {
 | 
			
		||||
		result = append(result, member.Username)
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
 | 
			
		||||
	var cookies []*http.Cookie
 | 
			
		||||
	jar, _ := cookiejar.New(nil)
 | 
			
		||||
	firstCookie := &http.Cookie{
 | 
			
		||||
		Name:   "MMAUTHTOKEN",
 | 
			
		||||
		Value:  token,
 | 
			
		||||
		Path:   "/",
 | 
			
		||||
		Domain: m.Credentials.Server,
 | 
			
		||||
	}
 | 
			
		||||
	cookies = append(cookies, firstCookie)
 | 
			
		||||
	cookieURL, _ := url.Parse("https://" + m.Credentials.Server)
 | 
			
		||||
	jar.SetCookies(cookieURL, cookies)
 | 
			
		||||
	return jar
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendDirectMessage sends a direct message to specified user
 | 
			
		||||
func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
 | 
			
		||||
	m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
 | 
			
		||||
	// create DM channel (only happens on first message)
 | 
			
		||||
	_, err := m.Client.CreateDirectChannel(toUserId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
 | 
			
		||||
	}
 | 
			
		||||
	channelName := model.GetDMNameFromIds(toUserId, m.User.Id)
 | 
			
		||||
 | 
			
		||||
	// update our channels
 | 
			
		||||
	mmchannels, _ := m.Client.GetChannels("")
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	m.Team.Channels = mmchannels.Data.(*model.ChannelList)
 | 
			
		||||
	m.Unlock()
 | 
			
		||||
 | 
			
		||||
	// build & send the message
 | 
			
		||||
	msg = strings.Replace(msg, "\r", "", -1)
 | 
			
		||||
	post := &model.Post{ChannelId: m.GetChannelId(channelName, ""), Message: msg}
 | 
			
		||||
	m.Client.CreatePost(post)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTeamName returns the name of the specified teamId
 | 
			
		||||
func (m *MMClient) GetTeamName(teamId string) string {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		if t.Id == teamId {
 | 
			
		||||
			return t.Team.Name
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannels returns all channels we're members off
 | 
			
		||||
func (m *MMClient) GetChannels() []*model.Channel {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	var channels []*model.Channel
 | 
			
		||||
	// our primary team channels first
 | 
			
		||||
	channels = append(channels, m.Team.Channels.Channels...)
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		if t.Id != m.Team.Id {
 | 
			
		||||
			channels = append(channels, t.Channels.Channels...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return channels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMoreChannels returns existing channels where we're not a member off.
 | 
			
		||||
func (m *MMClient) GetMoreChannels() []*model.Channel {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	var channels []*model.Channel
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		channels = append(channels, t.MoreChannels.Channels...)
 | 
			
		||||
	}
 | 
			
		||||
	return channels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTeamFromChannel returns teamId belonging to channel (DM channels have no teamId).
 | 
			
		||||
func (m *MMClient) GetTeamFromChannel(channelId string) string {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	var channels []*model.Channel
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		channels = append(channels, t.Channels.Channels...)
 | 
			
		||||
		channels = append(channels, t.MoreChannels.Channels...)
 | 
			
		||||
		for _, c := range channels {
 | 
			
		||||
			if c.Id == channelId {
 | 
			
		||||
				return t.Id
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetLastViewedAt(channelId string) int64 {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	for _, t := range m.OtherTeams {
 | 
			
		||||
		if _, ok := t.Channels.Members[channelId]; ok {
 | 
			
		||||
			return t.Channels.Members[channelId].LastViewedAt
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetUsers() map[string]*model.User {
 | 
			
		||||
	users := make(map[string]*model.User)
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	for k, v := range m.Users {
 | 
			
		||||
		users[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	return users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetUser(userId string) *model.User {
 | 
			
		||||
	m.RLock()
 | 
			
		||||
	defer m.RUnlock()
 | 
			
		||||
	return m.Users[userId]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetStatus(userId string) string {
 | 
			
		||||
	res, err := m.Client.GetStatuses()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	status := res.Data.(map[string]string)
 | 
			
		||||
	if status[userId] == model.STATUS_AWAY {
 | 
			
		||||
		return "away"
 | 
			
		||||
	}
 | 
			
		||||
	if status[userId] == model.STATUS_ONLINE {
 | 
			
		||||
		return "online"
 | 
			
		||||
	}
 | 
			
		||||
	return "offline"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) StatusLoop() {
 | 
			
		||||
	for {
 | 
			
		||||
		if m.WsQuit {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if m.WsConnected {
 | 
			
		||||
			m.log.Debug("WS PING")
 | 
			
		||||
			m.sendWSRequest("ping", nil)
 | 
			
		||||
			select {
 | 
			
		||||
			case <-m.WsPingChan:
 | 
			
		||||
				m.log.Debug("WS PONG received")
 | 
			
		||||
			case <-time.After(time.Second * 5):
 | 
			
		||||
				m.Logout()
 | 
			
		||||
				m.WsQuit = false
 | 
			
		||||
				m.Login()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		time.Sleep(time.Second * 60)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize user and teams
 | 
			
		||||
func (m *MMClient) initUser() error {
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	defer m.Unlock()
 | 
			
		||||
	m.log.Debug("initUser()")
 | 
			
		||||
	initLoad, err := m.Client.GetInitialLoad()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	initData := initLoad.Data.(*model.InitialLoad)
 | 
			
		||||
	m.User = initData.User
 | 
			
		||||
	// we only load all team data on initial login.
 | 
			
		||||
	// all other updates are for channels from our (primary) team only.
 | 
			
		||||
	m.log.Debug("initUser(): loading all team data")
 | 
			
		||||
	for _, v := range initData.Teams {
 | 
			
		||||
		m.Client.SetTeamId(v.Id)
 | 
			
		||||
		mmusers, _ := m.Client.GetProfiles(v.Id, "")
 | 
			
		||||
		t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id}
 | 
			
		||||
		mmchannels, _ := m.Client.GetChannels("")
 | 
			
		||||
		t.Channels = mmchannels.Data.(*model.ChannelList)
 | 
			
		||||
		mmchannels, _ = m.Client.GetMoreChannels("")
 | 
			
		||||
		t.MoreChannels = mmchannels.Data.(*model.ChannelList)
 | 
			
		||||
		m.OtherTeams = append(m.OtherTeams, t)
 | 
			
		||||
		if v.Name == m.Credentials.Team {
 | 
			
		||||
			m.Team = t
 | 
			
		||||
			m.log.Debugf("initUser(): found our team %s (id: %s)", v.Name, v.Id)
 | 
			
		||||
		}
 | 
			
		||||
		// add all users
 | 
			
		||||
		for k, v := range t.Users {
 | 
			
		||||
			m.Users[k] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) sendWSRequest(action string, data map[string]interface{}) error {
 | 
			
		||||
	req := &model.WebSocketRequest{}
 | 
			
		||||
	req.Seq = m.WsSequence
 | 
			
		||||
	req.Action = action
 | 
			
		||||
	req.Data = data
 | 
			
		||||
	m.WsSequence++
 | 
			
		||||
	m.log.Debugf("sendWsRequest %#v", req)
 | 
			
		||||
	m.WsClient.WriteJSON(req)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -10,8 +10,8 @@ import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OMessage for mattermost incoming webhook. (send to mattermost)
 | 
			
		||||
@@ -27,15 +27,19 @@ type OMessage struct {
 | 
			
		||||
 | 
			
		||||
// IMessage for mattermost outgoing webhook. (received from mattermost)
 | 
			
		||||
type IMessage struct {
 | 
			
		||||
	BotID       string `schema:"bot_id"`
 | 
			
		||||
	BotName     string `schema:"bot_name"`
 | 
			
		||||
	Token       string `schema:"token"`
 | 
			
		||||
	TeamID      string `schema:"team_id"`
 | 
			
		||||
	TeamDomain  string `schema:"team_domain"`
 | 
			
		||||
	ChannelID   string `schema:"channel_id"`
 | 
			
		||||
	ServiceID   string `schema:"service_id"`
 | 
			
		||||
	ChannelName string `schema:"channel_name"`
 | 
			
		||||
	Timestamp   string `schema:"timestamp"`
 | 
			
		||||
	UserID      string `schema:"user_id"`
 | 
			
		||||
	UserName    string `schema:"user_name"`
 | 
			
		||||
	PostId      string `schema:"post_id"`
 | 
			
		||||
	RawText     string `schema:"raw_text"`
 | 
			
		||||
	ServiceId   string `schema:"service_id"`
 | 
			
		||||
	Text        string `schema:"text"`
 | 
			
		||||
	TriggerWord string `schema:"trigger_word"`
 | 
			
		||||
}
 | 
			
		||||
@@ -51,7 +55,6 @@ type Client struct {
 | 
			
		||||
 | 
			
		||||
// Config for client.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	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
 | 
			
		||||
@@ -61,15 +64,15 @@ type Config struct {
 | 
			
		||||
// New Mattermost client.
 | 
			
		||||
func New(url string, config Config) *Client {
 | 
			
		||||
	c := &Client{Url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config}
 | 
			
		||||
	if c.Port == 0 {
 | 
			
		||||
		c.Port = 9999
 | 
			
		||||
	}
 | 
			
		||||
	c.BindAddress += ":"
 | 
			
		||||
	tr := &http.Transport{
 | 
			
		||||
		TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify},
 | 
			
		||||
	}
 | 
			
		||||
	c.httpclient = &http.Client{Transport: tr}
 | 
			
		||||
	if !c.DisableServer {
 | 
			
		||||
		_, _, err := net.SplitHostPort(c.BindAddress)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("incorrect bindaddress %s", c.BindAddress)
 | 
			
		||||
		}
 | 
			
		||||
		go c.StartServer()
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
@@ -79,8 +82,8 @@ func New(url string, config Config) *Client {
 | 
			
		||||
func (c *Client) StartServer() {
 | 
			
		||||
	mux := http.NewServeMux()
 | 
			
		||||
	mux.Handle("/", c)
 | 
			
		||||
	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.Printf("Listening on http://%v...\n", c.BindAddress)
 | 
			
		||||
	if err := http.ListenAndServe(c.BindAddress, mux); err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								migration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								migration.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
# Breaking changes from 0.4 to 0.5 for matterbridge (webhooks version)
 | 
			
		||||
## IRC section
 | 
			
		||||
### Server
 | 
			
		||||
Port removed, added to server
 | 
			
		||||
```
 | 
			
		||||
server="irc.freenode.net"
 | 
			
		||||
port=6667
 | 
			
		||||
```
 | 
			
		||||
changed to
 | 
			
		||||
```
 | 
			
		||||
server="irc.freenode.net:6667"
 | 
			
		||||
```
 | 
			
		||||
### Channel
 | 
			
		||||
Removed see Channels section below
 | 
			
		||||
 | 
			
		||||
### UseSlackCircumfix=true
 | 
			
		||||
Removed, can be done by using ```RemoteNickFormat="<{NICK}> "```
 | 
			
		||||
 | 
			
		||||
## Mattermost section
 | 
			
		||||
### BindAddress
 | 
			
		||||
Port removed, added to BindAddress
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
BindAddress="0.0.0.0"
 | 
			
		||||
port=9999
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
changed to
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
BindAddress="0.0.0.0:9999"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Token
 | 
			
		||||
Removed
 | 
			
		||||
 | 
			
		||||
## Channels section
 | 
			
		||||
```
 | 
			
		||||
[Token "outgoingwebhooktoken1"] 
 | 
			
		||||
IRCChannel="#off-topic"
 | 
			
		||||
MMChannel="off-topic"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
changed to
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[Channel "channelnameofchoice"] 
 | 
			
		||||
IRC="#off-topic"
 | 
			
		||||
Mattermost="off-topic"
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										169
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -21,15 +21,17 @@ type MMhook struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MMapi struct {
 | 
			
		||||
	mc    *matterclient.MMClient
 | 
			
		||||
	mmMap map[string]string
 | 
			
		||||
	mc            *matterclient.MMClient
 | 
			
		||||
	mmMap         map[string]string
 | 
			
		||||
	mmIgnoreNicks []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MMirc struct {
 | 
			
		||||
	i       *irc.Connection
 | 
			
		||||
	ircNick string
 | 
			
		||||
	ircMap  map[string]string
 | 
			
		||||
	names   map[string][]string
 | 
			
		||||
	i              *irc.Connection
 | 
			
		||||
	ircNick        string
 | 
			
		||||
	ircMap         map[string]string
 | 
			
		||||
	names          map[string][]string
 | 
			
		||||
	ircIgnoreNicks []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MMMessage struct {
 | 
			
		||||
@@ -53,6 +55,8 @@ type FancyLog struct {
 | 
			
		||||
 | 
			
		||||
var flog FancyLog
 | 
			
		||||
 | 
			
		||||
const Legacy = "legacy"
 | 
			
		||||
 | 
			
		||||
func initFLog() {
 | 
			
		||||
	flog.irc = log.WithFields(log.Fields{"module": "irc"})
 | 
			
		||||
	flog.mm = log.WithFields(log.Fields{"module": "mattermost"})
 | 
			
		||||
@@ -66,7 +70,9 @@ func NewBridge(name string, config *Config, kind string) *Bridge {
 | 
			
		||||
	b.ircNick = b.Config.IRC.Nick
 | 
			
		||||
	b.ircMap = make(map[string]string)
 | 
			
		||||
	b.MMirc.names = make(map[string][]string)
 | 
			
		||||
	if kind == "legacy" {
 | 
			
		||||
	b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks)
 | 
			
		||||
	b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks)
 | 
			
		||||
	if kind == Legacy {
 | 
			
		||||
		if len(b.Config.Token) > 0 {
 | 
			
		||||
			for _, val := range b.Config.Token {
 | 
			
		||||
				b.ircMap[val.IRCChannel] = val.MMChannel
 | 
			
		||||
@@ -87,10 +93,14 @@ func NewBridge(name string, config *Config, kind string) *Bridge {
 | 
			
		||||
		}
 | 
			
		||||
		b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password,
 | 
			
		||||
			b.Config.Mattermost.Team, b.Config.Mattermost.Server)
 | 
			
		||||
		b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify
 | 
			
		||||
		b.mc.NoTLS = b.Config.Mattermost.NoTLS
 | 
			
		||||
		flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server)
 | 
			
		||||
		err := b.mc.Login()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.mm.Fatal("can not connect", err)
 | 
			
		||||
			flog.mm.Fatal("Can not connect", err)
 | 
			
		||||
		}
 | 
			
		||||
		flog.mm.Info("Login ok")
 | 
			
		||||
		b.mc.JoinChannel(b.Config.Mattermost.Channel)
 | 
			
		||||
		if len(b.Config.Channel) > 0 {
 | 
			
		||||
			for _, val := range b.Config.Channel {
 | 
			
		||||
@@ -99,7 +109,9 @@ func NewBridge(name string, config *Config, kind string) *Bridge {
 | 
			
		||||
		}
 | 
			
		||||
		go b.mc.WsReceiver()
 | 
			
		||||
	}
 | 
			
		||||
	flog.irc.Info("Trying IRC connection")
 | 
			
		||||
	b.i = b.createIRC(name)
 | 
			
		||||
	flog.irc.Info("Connection succeeded")
 | 
			
		||||
	go b.handleMatter()
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
@@ -111,37 +123,51 @@ func (b *Bridge) createIRC(name string) *irc.Connection {
 | 
			
		||||
	if b.Config.IRC.Password != "" {
 | 
			
		||||
		i.Password = b.Config.IRC.Password
 | 
			
		||||
	}
 | 
			
		||||
	i.AddCallback("*", b.handleOther)
 | 
			
		||||
	i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection)
 | 
			
		||||
	i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port))
 | 
			
		||||
	return i
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleNewConnection(event *irc.Event) {
 | 
			
		||||
	flog.irc.Info("Registering callbacks")
 | 
			
		||||
	i := b.i
 | 
			
		||||
	b.ircNick = event.Arguments[0]
 | 
			
		||||
	i.AddCallback("PRIVMSG", b.handlePrivMsg)
 | 
			
		||||
	i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
 | 
			
		||||
	i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames)
 | 
			
		||||
	i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
 | 
			
		||||
	i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
 | 
			
		||||
	i.AddCallback(ircm.NOTICE, b.handleNotice)
 | 
			
		||||
	i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) })
 | 
			
		||||
	i.AddCallback("PING", func(e *irc.Event) {
 | 
			
		||||
		i.SendRaw("PONG :" + e.Message())
 | 
			
		||||
		flog.irc.Debugf("PING/PONG")
 | 
			
		||||
	})
 | 
			
		||||
	if b.Config.Mattermost.ShowJoinPart {
 | 
			
		||||
		i.AddCallback("JOIN", b.handleJoinPart)
 | 
			
		||||
		i.AddCallback("PART", b.handleJoinPart)
 | 
			
		||||
	}
 | 
			
		||||
	i.AddCallback("*", b.handleOther)
 | 
			
		||||
	b.setupChannels()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) setupChannels() {
 | 
			
		||||
	i := b.i
 | 
			
		||||
	flog.irc.Info("Joining ", b.Config.IRC.Channel, " as ", b.ircNick)
 | 
			
		||||
	i.Join(b.Config.IRC.Channel)
 | 
			
		||||
	if b.kind == "legacy" {
 | 
			
		||||
	if b.Config.IRC.Channel != "" {
 | 
			
		||||
		flog.irc.Infof("Joining %s as %s", b.Config.IRC.Channel, b.ircNick)
 | 
			
		||||
		i.Join(b.Config.IRC.Channel)
 | 
			
		||||
	}
 | 
			
		||||
	if b.kind == Legacy {
 | 
			
		||||
		for _, val := range b.Config.Token {
 | 
			
		||||
			flog.irc.Info("Joining ", val.IRCChannel, " as ", b.ircNick)
 | 
			
		||||
			flog.irc.Infof("Joining %s as %s", val.IRCChannel, b.ircNick)
 | 
			
		||||
			i.Join(val.IRCChannel)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for _, val := range b.Config.Channel {
 | 
			
		||||
			flog.irc.Info("Joining ", val.IRC, " as ", b.ircNick)
 | 
			
		||||
			flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick)
 | 
			
		||||
			i.Join(val.IRC)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool {
 | 
			
		||||
@@ -177,6 +203,9 @@ func (b *Bridge) ircNickFormat(nick string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handlePrivMsg(event *irc.Event) {
 | 
			
		||||
	if b.ignoreMessage(event.Nick, event.Message(), "irc") {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if b.handleIrcBotCommand(event) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -238,81 +267,21 @@ func (b *Bridge) endNames(event *irc.Event) {
 | 
			
		||||
	b.MMirc.names[channel] = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleTopicWhoTime(event *irc.Event) bool {
 | 
			
		||||
func (b *Bridge) handleTopicWhoTime(event *irc.Event) {
 | 
			
		||||
	parts := strings.Split(event.Arguments[2], "!")
 | 
			
		||||
	t_i, err := strconv.ParseInt(event.Arguments[3], 10, 64)
 | 
			
		||||
	t, err := strconv.ParseInt(event.Arguments[3], 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3])
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	user := parts[0]
 | 
			
		||||
	if len(parts) > 1 {
 | 
			
		||||
		user += " [" + parts[1] + "]"
 | 
			
		||||
	}
 | 
			
		||||
	flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t_i, 0))
 | 
			
		||||
	return true
 | 
			
		||||
	flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleOther(event *irc.Event) {
 | 
			
		||||
	flog.irc.Debugf("%#v", event)
 | 
			
		||||
	switch event.Code {
 | 
			
		||||
	case ircm.RPL_WELCOME:
 | 
			
		||||
		b.handleNewConnection(event)
 | 
			
		||||
	case ircm.RPL_ENDOFNAMES:
 | 
			
		||||
		b.endNames(event)
 | 
			
		||||
	case ircm.RPL_NAMREPLY:
 | 
			
		||||
		b.storeNames(event)
 | 
			
		||||
	case ircm.RPL_ISUPPORT:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_LUSEROP:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_LUSERUNKNOWN:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_LUSERCHANNELS:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_MYINFO:
 | 
			
		||||
		flog.irc.Infof("%s: %s", event.Code, strings.Join(event.Arguments[1:], " "))
 | 
			
		||||
	case ircm.RPL_YOURHOST:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_CREATED:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_STATSDLINE:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_LUSERCLIENT:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_LUSERME:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_LOCALUSERS:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_GLOBALUSERS:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.RPL_MOTD:
 | 
			
		||||
		flog.irc.Infof("%s: %s", event.Code, event.Message())
 | 
			
		||||
		// flog.irc.Info(event.Message())
 | 
			
		||||
	case ircm.RPL_TOPIC:
 | 
			
		||||
		flog.irc.Infof("%s: Topic for %s: %s", event.Code, event.Arguments[1], event.Message())
 | 
			
		||||
	case ircm.RPL_TOPICWHOTIME:
 | 
			
		||||
		if !b.handleTopicWhoTime(event) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	case ircm.MODE:
 | 
			
		||||
		flog.irc.Infof("%s: %s %s", event.Code, event.Arguments[1], event.Arguments[0])
 | 
			
		||||
	case ircm.JOIN:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.PING:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	case ircm.PONG:
 | 
			
		||||
		flog.irc.Infof("%s: %s", event.Code, event.Message())
 | 
			
		||||
	case ircm.RPL_ENDOFMOTD:
 | 
			
		||||
	case ircm.RPL_MOTDSTART:
 | 
			
		||||
	case ircm.ERR_NICKNAMEINUSE:
 | 
			
		||||
		flog.irc.Warn(event.Message())
 | 
			
		||||
	case ircm.NOTICE:
 | 
			
		||||
		b.handleNotice(event)
 | 
			
		||||
	default:
 | 
			
		||||
		flog.irc.Infof("UNKNOWN EVENT: %#v", event)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) Send(nick string, message string, channel string) error {
 | 
			
		||||
@@ -327,7 +296,7 @@ func (b *Bridge) SendType(nick string, message string, channel string, mtype str
 | 
			
		||||
			message = nick + " " + message
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if b.kind == "legacy" {
 | 
			
		||||
	if b.kind == Legacy {
 | 
			
		||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL}
 | 
			
		||||
		matterMessage.Channel = channel
 | 
			
		||||
		matterMessage.UserName = nick
 | 
			
		||||
@@ -371,24 +340,34 @@ func (b *Bridge) handleMatterClient(mchan chan *MMMessage) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) handleMatter() {
 | 
			
		||||
	flog.mm.Infof("Choosing Mattermost connection type %s", b.kind)
 | 
			
		||||
	mchan := make(chan *MMMessage)
 | 
			
		||||
	if b.kind == "legacy" {
 | 
			
		||||
	if b.kind == Legacy {
 | 
			
		||||
		go b.handleMatterHook(mchan)
 | 
			
		||||
	} else {
 | 
			
		||||
		go b.handleMatterClient(mchan)
 | 
			
		||||
	}
 | 
			
		||||
	flog.mm.Info("Start listening for Mattermost messages")
 | 
			
		||||
	for message := range mchan {
 | 
			
		||||
		var username string
 | 
			
		||||
		if b.ignoreMessage(message.Username, message.Text, "mattermost") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		username = message.Username + ": "
 | 
			
		||||
		if b.Config.IRC.RemoteNickFormat != "" {
 | 
			
		||||
			username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1)
 | 
			
		||||
		} else if b.Config.IRC.UseSlackCircumfix {
 | 
			
		||||
			username = "<" + message.Username + "> "
 | 
			
		||||
		}
 | 
			
		||||
		cmd := strings.Fields(message.Text)[0]
 | 
			
		||||
		cmds := strings.Fields(message.Text)
 | 
			
		||||
		// empty message
 | 
			
		||||
		if len(cmds) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cmd := cmds[0]
 | 
			
		||||
		switch cmd {
 | 
			
		||||
		case "!users":
 | 
			
		||||
			flog.mm.Info("received !users from ", message.Username)
 | 
			
		||||
			flog.mm.Info("Received !users from ", message.Username)
 | 
			
		||||
			b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel))
 | 
			
		||||
			continue
 | 
			
		||||
		case "!gif":
 | 
			
		||||
@@ -425,7 +404,7 @@ func (b *Bridge) getMMChannel(ircChannel string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) getIRCChannel(channel string) string {
 | 
			
		||||
	if b.kind == "legacy" {
 | 
			
		||||
	if b.kind == Legacy {
 | 
			
		||||
		ircchannel := b.Config.IRC.Channel
 | 
			
		||||
		_, ok := b.Config.Token[channel]
 | 
			
		||||
		if ok {
 | 
			
		||||
@@ -439,3 +418,17 @@ func (b *Bridge) getIRCChannel(channel string) string {
 | 
			
		||||
	}
 | 
			
		||||
	return ircchannel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bridge) ignoreMessage(nick string, message string, protocol string) bool {
 | 
			
		||||
	var ignoreNicks = b.mmIgnoreNicks
 | 
			
		||||
	if protocol == "irc" {
 | 
			
		||||
		ignoreNicks = b.ircIgnoreNicks
 | 
			
		||||
	}
 | 
			
		||||
	// should we discard messages ?
 | 
			
		||||
	for _, entry := range ignoreNicks {
 | 
			
		||||
		if nick == entry {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/42wim/matterbridge-plus/bridge/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -19,6 +19,7 @@ type Config struct {
 | 
			
		||||
		NickServNick      string
 | 
			
		||||
		NickServPassword  string
 | 
			
		||||
		RemoteNickFormat  string
 | 
			
		||||
		IgnoreNicks       string
 | 
			
		||||
	}
 | 
			
		||||
	Mattermost struct {
 | 
			
		||||
		URL                    string
 | 
			
		||||
@@ -37,6 +38,8 @@ type Config struct {
 | 
			
		||||
		Login                  string
 | 
			
		||||
		Password               string
 | 
			
		||||
		RemoteNickFormat       *string
 | 
			
		||||
		IgnoreNicks            string
 | 
			
		||||
		NoTLS                  bool
 | 
			
		||||
	}
 | 
			
		||||
	Token map[string]*struct {
 | 
			
		||||
		IRCChannel string
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										167
									
								
								vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,9 +1,12 @@
 | 
			
		||||
package matterclient
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/cookiejar"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -13,11 +16,12 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Credentials struct {
 | 
			
		||||
	Login  string
 | 
			
		||||
	Team   string
 | 
			
		||||
	Pass   string
 | 
			
		||||
	Server string
 | 
			
		||||
	NoTLS  bool
 | 
			
		||||
	Login         string
 | 
			
		||||
	Team          string
 | 
			
		||||
	Pass          string
 | 
			
		||||
	Server        string
 | 
			
		||||
	NoTLS         bool
 | 
			
		||||
	SkipTLSVerify bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
@@ -33,6 +37,8 @@ type MMClient struct {
 | 
			
		||||
	*Credentials
 | 
			
		||||
	Client       *model.Client
 | 
			
		||||
	WsClient     *websocket.Conn
 | 
			
		||||
	WsQuit       bool
 | 
			
		||||
	WsAway       bool
 | 
			
		||||
	Channels     *model.ChannelList
 | 
			
		||||
	MoreChannels *model.ChannelList
 | 
			
		||||
	User         *model.User
 | 
			
		||||
@@ -60,6 +66,9 @@ func (m *MMClient) SetLogLevel(level string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) Login() error {
 | 
			
		||||
	if m.WsQuit {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	b := &backoff.Backoff{
 | 
			
		||||
		Min:    time.Second,
 | 
			
		||||
		Max:    5 * time.Minute,
 | 
			
		||||
@@ -73,12 +82,25 @@ func (m *MMClient) Login() error {
 | 
			
		||||
	}
 | 
			
		||||
	// login to mattermost
 | 
			
		||||
	m.Client = model.NewClient(uriScheme + m.Credentials.Server)
 | 
			
		||||
	m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
 | 
			
		||||
	var myinfo *model.Result
 | 
			
		||||
	var appErr *model.AppError
 | 
			
		||||
	var logmsg = "trying login"
 | 
			
		||||
	for {
 | 
			
		||||
		m.log.Debugf(logmsg+" %s %s %s", m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
 | 
			
		||||
		myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
 | 
			
		||||
		m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
 | 
			
		||||
		if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
 | 
			
		||||
			m.log.Debugf(logmsg+" with ", model.SESSION_COOKIE_TOKEN)
 | 
			
		||||
			token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=")
 | 
			
		||||
			m.Client.HttpClient.Jar = m.createCookieJar(token[1])
 | 
			
		||||
			m.Client.MockSession(token[1])
 | 
			
		||||
			myinfo, appErr = m.Client.GetMe("")
 | 
			
		||||
			if myinfo.Data.(*model.User) == nil {
 | 
			
		||||
				m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
 | 
			
		||||
				return errors.New("invalid " + model.SESSION_COOKIE_TOKEN)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
 | 
			
		||||
		}
 | 
			
		||||
		if appErr != nil {
 | 
			
		||||
			d := b.Duration()
 | 
			
		||||
			m.log.Debug(appErr.DetailedError)
 | 
			
		||||
@@ -89,7 +111,7 @@ func (m *MMClient) Login() error {
 | 
			
		||||
				}
 | 
			
		||||
				return errors.New(appErr.Message)
 | 
			
		||||
			}
 | 
			
		||||
			m.log.Debug("LOGIN: %s, reconnecting in %s", appErr, d)
 | 
			
		||||
			m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
 | 
			
		||||
			time.Sleep(d)
 | 
			
		||||
			logmsg = "retrying login"
 | 
			
		||||
			continue
 | 
			
		||||
@@ -98,15 +120,16 @@ func (m *MMClient) Login() error {
 | 
			
		||||
	}
 | 
			
		||||
	// reset timer
 | 
			
		||||
	b.Reset()
 | 
			
		||||
	m.User = myinfo.Data.(*model.User)
 | 
			
		||||
 | 
			
		||||
	teamdata, _ := m.Client.GetAllTeamListings()
 | 
			
		||||
	teams := teamdata.Data.(map[string]*model.Team)
 | 
			
		||||
	for k, v := range teams {
 | 
			
		||||
	initLoad, _ := m.Client.GetInitialLoad()
 | 
			
		||||
	initData := initLoad.Data.(*model.InitialLoad)
 | 
			
		||||
	m.User = initData.User
 | 
			
		||||
	for _, v := range initData.Teams {
 | 
			
		||||
		m.log.Debugf("trying %s (id: %s)", v.Name, v.Id)
 | 
			
		||||
		if v.Name == m.Credentials.Team {
 | 
			
		||||
			m.Client.SetTeamId(k)
 | 
			
		||||
			m.Client.SetTeamId(v.Id)
 | 
			
		||||
			m.Team = v
 | 
			
		||||
			m.log.Debug("GetallTeamListings: found id ", k)
 | 
			
		||||
			m.log.Debugf("GetallTeamListings: found id %s for team %s", v.Id, v.Name)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -119,13 +142,14 @@ func (m *MMClient) Login() error {
 | 
			
		||||
	header := http.Header{}
 | 
			
		||||
	header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
 | 
			
		||||
 | 
			
		||||
	var WsClient *websocket.Conn
 | 
			
		||||
	m.log.Debug("WsClient: making connection")
 | 
			
		||||
	var err error
 | 
			
		||||
	for {
 | 
			
		||||
		WsClient, _, err = websocket.DefaultDialer.Dial(wsurl, header)
 | 
			
		||||
		wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
 | 
			
		||||
		m.WsClient, _, err = wsDialer.Dial(wsurl, header)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			d := b.Duration()
 | 
			
		||||
			log.Printf("WSS: %s, reconnecting in %s", err, d)
 | 
			
		||||
			m.log.Debugf("WSS: %s, reconnecting in %s", err, d)
 | 
			
		||||
			time.Sleep(d)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -133,8 +157,6 @@ func (m *MMClient) Login() error {
 | 
			
		||||
	}
 | 
			
		||||
	b.Reset()
 | 
			
		||||
 | 
			
		||||
	m.WsClient = WsClient
 | 
			
		||||
 | 
			
		||||
	// populating users
 | 
			
		||||
	m.UpdateUsers()
 | 
			
		||||
 | 
			
		||||
@@ -147,12 +169,19 @@ func (m *MMClient) Login() error {
 | 
			
		||||
func (m *MMClient) WsReceiver() {
 | 
			
		||||
	var rmsg model.Message
 | 
			
		||||
	for {
 | 
			
		||||
		if m.WsQuit {
 | 
			
		||||
			m.log.Debug("exiting WsReceiver")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if err := m.WsClient.ReadJSON(&rmsg); err != nil {
 | 
			
		||||
			log.Println("error:", err)
 | 
			
		||||
			m.log.Error("error:", err)
 | 
			
		||||
			// reconnect
 | 
			
		||||
			m.Login()
 | 
			
		||||
		}
 | 
			
		||||
		//log.Printf("WsReceiver: %#v", rmsg)
 | 
			
		||||
		if rmsg.Action == "ping" {
 | 
			
		||||
			m.handleWsPing()
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		msg := &Message{Raw: &rmsg, Team: m.Credentials.Team}
 | 
			
		||||
		m.parseMessage(msg)
 | 
			
		||||
		m.MessageChan <- msg
 | 
			
		||||
@@ -160,6 +189,14 @@ func (m *MMClient) WsReceiver() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) handleWsPing() {
 | 
			
		||||
	m.log.Debug("Ws PING")
 | 
			
		||||
	if !m.WsQuit && !m.WsAway {
 | 
			
		||||
		m.log.Debug("Ws PONG")
 | 
			
		||||
		m.WsClient.WriteMessage(websocket.PongMessage, []byte{})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) parseMessage(rmsg *Message) {
 | 
			
		||||
	switch rmsg.Raw.Action {
 | 
			
		||||
	case model.ACTION_POSTED:
 | 
			
		||||
@@ -198,7 +235,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateUsers() error {
 | 
			
		||||
	mmusers, _ := m.Client.GetProfiles(m.Client.GetTeamId(), "")
 | 
			
		||||
	mmusers, _ := m.Client.GetProfilesForDirectMessageList(m.Team.Id)
 | 
			
		||||
	m.Users = mmusers.Data.(map[string]*model.User)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -294,29 +331,49 @@ func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList {
 | 
			
		||||
	return res.Data.(*model.PostList)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetPublicLink(filename string) string {
 | 
			
		||||
	res, err := m.Client.GetPublicLink(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return res.Data.(string)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetPublicLinks(filenames []string) []string {
 | 
			
		||||
	var output []string
 | 
			
		||||
	for _, f := range filenames {
 | 
			
		||||
		res, err := m.Client.GetPublicLink(f)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		output = append(output, res.Data.(string))
 | 
			
		||||
	}
 | 
			
		||||
	return output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
 | 
			
		||||
	data := make(map[string]string)
 | 
			
		||||
	data["channel_id"] = channelId
 | 
			
		||||
	data["channel_header"] = header
 | 
			
		||||
	log.Printf("updating channelheader %#v, %#v", channelId, header)
 | 
			
		||||
	m.log.Debugf("updating channelheader %#v, %#v", channelId, header)
 | 
			
		||||
	_, err := m.Client.UpdateChannelHeader(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Print(err)
 | 
			
		||||
		log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
			
		||||
	log.Printf("posting lastview %#v", channelId)
 | 
			
		||||
	m.log.Debugf("posting lastview %#v", channelId)
 | 
			
		||||
	_, err := m.Client.UpdateLastViewedAt(channelId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Print(err)
 | 
			
		||||
		m.log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) UsernamesInChannel(channelName string) []string {
 | 
			
		||||
	ceiRes, err := m.Client.GetChannelExtraInfo(m.GetChannelId(channelName), 5000, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Errorf("UsernamesInChannel(%s) failed: %s", channelName, err)
 | 
			
		||||
		m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelName, err)
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
	extra := ceiRes.Data.(*model.ChannelExtra)
 | 
			
		||||
@@ -326,3 +383,59 @@ func (m *MMClient) UsernamesInChannel(channelName string) []string {
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
 | 
			
		||||
	var cookies []*http.Cookie
 | 
			
		||||
	jar, _ := cookiejar.New(nil)
 | 
			
		||||
	firstCookie := &http.Cookie{
 | 
			
		||||
		Name:   "MMAUTHTOKEN",
 | 
			
		||||
		Value:  token,
 | 
			
		||||
		Path:   "/",
 | 
			
		||||
		Domain: m.Credentials.Server,
 | 
			
		||||
	}
 | 
			
		||||
	cookies = append(cookies, firstCookie)
 | 
			
		||||
	cookieURL, _ := url.Parse("https://" + m.Credentials.Server)
 | 
			
		||||
	jar.SetCookies(cookieURL, cookies)
 | 
			
		||||
	return jar
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetOtherUserDM(channel string) *model.User {
 | 
			
		||||
	m.UpdateUsers()
 | 
			
		||||
	var rcvuser *model.User
 | 
			
		||||
	if strings.Contains(channel, "__") {
 | 
			
		||||
		rcvusers := strings.Split(channel, "__")
 | 
			
		||||
		if rcvusers[0] != m.User.Id {
 | 
			
		||||
			rcvuser = m.Users[rcvusers[0]]
 | 
			
		||||
		} else {
 | 
			
		||||
			rcvuser = m.Users[rcvusers[1]]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return rcvuser
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
 | 
			
		||||
	m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
 | 
			
		||||
	var channel string
 | 
			
		||||
	// We don't have a DM with this user yet.
 | 
			
		||||
	if m.GetChannelId(toUserId+"__"+m.User.Id) == "" && m.GetChannelId(m.User.Id+"__"+toUserId) == "" {
 | 
			
		||||
		// create DM channel
 | 
			
		||||
		_, err := m.Client.CreateDirectChannel(toUserId)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
 | 
			
		||||
		}
 | 
			
		||||
		// update our channels
 | 
			
		||||
		mmchannels, _ := m.Client.GetChannels("")
 | 
			
		||||
		m.Channels = mmchannels.Data.(*model.ChannelList)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// build the channel name
 | 
			
		||||
	if toUserId > m.User.Id {
 | 
			
		||||
		channel = m.User.Id + "__" + toUserId
 | 
			
		||||
	} else {
 | 
			
		||||
		channel = toUserId + "__" + m.User.Id
 | 
			
		||||
	}
 | 
			
		||||
	// build & send the message
 | 
			
		||||
	msg = strings.Replace(msg, "\r", "", -1)
 | 
			
		||||
	post := &model.Post{ChannelId: m.GetChannelId(channel), Message: msg}
 | 
			
		||||
	m.Client.CreatePost(post)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										154
									
								
								vendor/github.com/42wim/matterbridge/matterhook/matterhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										154
									
								
								vendor/github.com/42wim/matterbridge/matterhook/matterhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,154 +0,0 @@
 | 
			
		||||
//Package matterhook provides interaction with mattermost incoming/outgoing webhooks
 | 
			
		||||
package matterhook
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/gorilla/schema"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
	Attachments interface{} `json:"attachments,omitempty"`
 | 
			
		||||
	Type        string      `json:"type,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IMessage for mattermost outgoing webhook. (received from mattermost)
 | 
			
		||||
type IMessage struct {
 | 
			
		||||
	Token       string `schema:"token"`
 | 
			
		||||
	TeamID      string `schema:"team_id"`
 | 
			
		||||
	TeamDomain  string `schema:"team_domain"`
 | 
			
		||||
	ChannelID   string `schema:"channel_id"`
 | 
			
		||||
	ServiceID   string `schema:"service_id"`
 | 
			
		||||
	ChannelName string `schema:"channel_name"`
 | 
			
		||||
	Timestamp   string `schema:"timestamp"`
 | 
			
		||||
	UserID      string `schema:"user_id"`
 | 
			
		||||
	UserName    string `schema:"user_name"`
 | 
			
		||||
	Text        string `schema:"text"`
 | 
			
		||||
	TriggerWord string `schema:"trigger_word"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client for Mattermost.
 | 
			
		||||
type Client struct {
 | 
			
		||||
	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 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}
 | 
			
		||||
	if c.Port == 0 {
 | 
			
		||||
		c.Port = 9999
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StartServer starts a webserver listening for incoming mattermost POSTS.
 | 
			
		||||
func (c *Client) StartServer() {
 | 
			
		||||
	mux := http.NewServeMux()
 | 
			
		||||
	mux.Handle("/", c)
 | 
			
		||||
	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 {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		http.NotFound(w, r)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer r.Body.Close()
 | 
			
		||||
	decoder := schema.NewDecoder()
 | 
			
		||||
	err = decoder.Decode(&msg, r.PostForm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Receive returns an incoming message from mattermost outgoing webhooks URL.
 | 
			
		||||
func (c *Client) Receive() IMessage {
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case msg := <-c.In:
 | 
			
		||||
			return msg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send sends a msg to mattermost incoming webhooks URL.
 | 
			
		||||
func (c *Client) Send(msg OMessage) error {
 | 
			
		||||
	buf, err := json.Marshal(msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	resp, err := c.httpclient.Post(c.Url, "application/json", bytes.NewReader(buf))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	// Read entire body to completion to re-use keep-alive connections.
 | 
			
		||||
	io.Copy(ioutil.Discard, resp.Body)
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/mattermost/platform/einterfaces/account_migration.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/mattermost/platform/einterfaces/account_migration.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import "github.com/mattermost/platform/model"
 | 
			
		||||
 | 
			
		||||
type AccountMigrationInterface interface {
 | 
			
		||||
	MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string) *model.AppError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theAccountMigrationInterface AccountMigrationInterface
 | 
			
		||||
 | 
			
		||||
func RegisterAccountMigrationInterface(newInterface AccountMigrationInterface) {
 | 
			
		||||
	theAccountMigrationInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetAccountMigrationInterface() AccountMigrationInterface {
 | 
			
		||||
	return theAccountMigrationInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/mattermost/platform/einterfaces/brand.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/mattermost/platform/einterfaces/brand.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
	"mime/multipart"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BrandInterface interface {
 | 
			
		||||
	SaveBrandImage(*multipart.FileHeader) *model.AppError
 | 
			
		||||
	GetBrandImage() ([]byte, *model.AppError)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theBrandInterface BrandInterface
 | 
			
		||||
 | 
			
		||||
func RegisterBrandInterface(newInterface BrandInterface) {
 | 
			
		||||
	theBrandInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetBrandInterface() BrandInterface {
 | 
			
		||||
	return theBrandInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/mattermost/platform/einterfaces/cluster.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/mattermost/platform/einterfaces/cluster.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ClusterInterface interface {
 | 
			
		||||
	StartInterNodeCommunication()
 | 
			
		||||
	StopInterNodeCommunication()
 | 
			
		||||
	GetClusterInfos() []*model.ClusterInfo
 | 
			
		||||
	RemoveAllSessionsForUserId(userId string)
 | 
			
		||||
	InvalidateCacheForUser(userId string)
 | 
			
		||||
	InvalidateCacheForChannel(channelId string)
 | 
			
		||||
	Publish(event *model.WebSocketEvent)
 | 
			
		||||
	UpdateStatus(status *model.Status)
 | 
			
		||||
	GetLogs() ([]string, *model.AppError)
 | 
			
		||||
	GetClusterId() string
 | 
			
		||||
	ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theClusterInterface ClusterInterface
 | 
			
		||||
 | 
			
		||||
func RegisterClusterInterface(newInterface ClusterInterface) {
 | 
			
		||||
	theClusterInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetClusterInterface() ClusterInterface {
 | 
			
		||||
	return theClusterInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/mattermost/platform/einterfaces/compliance.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/mattermost/platform/einterfaces/compliance.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ComplianceInterface interface {
 | 
			
		||||
	StartComplianceDailyJob()
 | 
			
		||||
	RunComplianceJob(job *model.Compliance) *model.AppError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theComplianceInterface ComplianceInterface
 | 
			
		||||
 | 
			
		||||
func RegisterComplianceInterface(newInterface ComplianceInterface) {
 | 
			
		||||
	theComplianceInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetComplianceInterface() ComplianceInterface {
 | 
			
		||||
	return theComplianceInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/mattermost/platform/einterfaces/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/mattermost/platform/einterfaces/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EmojiInterface interface {
 | 
			
		||||
	CanUserCreateEmoji(string, []*model.TeamMember) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theEmojiInterface EmojiInterface
 | 
			
		||||
 | 
			
		||||
func RegisterEmojiInterface(newInterface EmojiInterface) {
 | 
			
		||||
	theEmojiInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetEmojiInterface() EmojiInterface {
 | 
			
		||||
	return theEmojiInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/mattermost/platform/einterfaces/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/mattermost/platform/einterfaces/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,11 @@ type LdapInterface interface {
 | 
			
		||||
	CheckPassword(id string, password string) *model.AppError
 | 
			
		||||
	SwitchToLdap(userId, ldapId, ldapPassword string) *model.AppError
 | 
			
		||||
	ValidateFilter(filter string) *model.AppError
 | 
			
		||||
	Syncronize() *model.AppError
 | 
			
		||||
	StartLdapSyncJob()
 | 
			
		||||
	SyncNow()
 | 
			
		||||
	RunTest() *model.AppError
 | 
			
		||||
	GetAllLdapUsers() ([]*model.User, *model.AppError)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theLdapInterface LdapInterface
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/mfa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/mfa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MfaInterface interface {
 | 
			
		||||
	GenerateQrCode(user *model.User) ([]byte, *model.AppError)
 | 
			
		||||
	Activate(user *model.User, token string) *model.AppError
 | 
			
		||||
	Deactivate(userId string) *model.AppError
 | 
			
		||||
	ValidateToken(secret, token string) (bool, *model.AppError)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theMfaInterface MfaInterface
 | 
			
		||||
 | 
			
		||||
func RegisterMfaInterface(newInterface MfaInterface) {
 | 
			
		||||
	theMfaInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetMfaInterface() MfaInterface {
 | 
			
		||||
	return theMfaInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/saml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/mattermost/platform/einterfaces/saml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package einterfaces
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/mattermost/platform/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SamlInterface interface {
 | 
			
		||||
	ConfigureSP() *model.AppError
 | 
			
		||||
	BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError)
 | 
			
		||||
	DoLogin(encodedXML string, relayState map[string]string) (*model.User, *model.AppError)
 | 
			
		||||
	GetMetadata() (string, *model.AppError)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var theSamlInterface SamlInterface
 | 
			
		||||
 | 
			
		||||
func RegisterSamlInterface(newInterface SamlInterface) {
 | 
			
		||||
	theSamlInterface = newInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetSamlInterface() SamlInterface {
 | 
			
		||||
	return theSamlInterface
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/mattermost/platform/model/access.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/mattermost/platform/model/access.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,10 +15,12 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AccessData struct {
 | 
			
		||||
	AuthCode     string `json:"auth_code"`
 | 
			
		||||
	ClientId     string `json:"client_id"`
 | 
			
		||||
	UserId       string `json:"user_id"`
 | 
			
		||||
	Token        string `json:"token"`
 | 
			
		||||
	RefreshToken string `json:"refresh_token"`
 | 
			
		||||
	RedirectUri  string `json:"redirect_uri"`
 | 
			
		||||
	ExpiresAt    int64  `json:"expires_at"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccessResponse struct {
 | 
			
		||||
@@ -33,8 +35,12 @@ type AccessResponse struct {
 | 
			
		||||
// correctly.
 | 
			
		||||
func (ad *AccessData) IsValid() *AppError {
 | 
			
		||||
 | 
			
		||||
	if len(ad.AuthCode) == 0 || len(ad.AuthCode) > 128 {
 | 
			
		||||
		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.auth_code.app_error", nil, "")
 | 
			
		||||
	if len(ad.ClientId) == 0 || len(ad.ClientId) > 26 {
 | 
			
		||||
		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.client_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ad.UserId) == 0 || len(ad.UserId) > 26 {
 | 
			
		||||
		return NewLocAppError("AccessData.IsValid", "model.access.is_valid.user_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ad.Token) != 26 {
 | 
			
		||||
@@ -52,6 +58,19 @@ func (ad *AccessData) IsValid() *AppError {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *AccessData) IsExpired() bool {
 | 
			
		||||
 | 
			
		||||
	if me.ExpiresAt <= 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if GetMillis() > me.ExpiresAt {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ad *AccessData) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(ad)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/mattermost/platform/model/authorize.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/mattermost/platform/model/authorize.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,6 +11,7 @@ import (
 | 
			
		||||
const (
 | 
			
		||||
	AUTHCODE_EXPIRE_TIME   = 60 * 10 // 10 minutes
 | 
			
		||||
	AUTHCODE_RESPONSE_TYPE = "code"
 | 
			
		||||
	DEFAULT_SCOPE          = "user"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AuthData struct {
 | 
			
		||||
@@ -71,6 +72,10 @@ func (ad *AuthData) PreSave() {
 | 
			
		||||
	if ad.CreateAt == 0 {
 | 
			
		||||
		ad.CreateAt = GetMillis()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ad.Scope) == 0 {
 | 
			
		||||
		ad.Scope = DEFAULT_SCOPE
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ad *AuthData) ToJson() string {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/mattermost/platform/model/channel.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/mattermost/platform/model/channel.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -124,9 +124,6 @@ func (o *Channel) ExtraUpdated() {
 | 
			
		||||
	o.ExtraUpdateAt = GetMillis()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Channel) PreExport() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetDMNameFromIds(userId1, userId2 string) string {
 | 
			
		||||
	if userId1 > userId2 {
 | 
			
		||||
		return userId2 + "__" + userId1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										651
									
								
								vendor/github.com/mattermost/platform/model/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										651
									
								
								vendor/github.com/mattermost/platform/model/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										66
									
								
								vendor/github.com/mattermost/platform/model/cluster_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/mattermost/platform/model/cluster_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ClusterInfo struct {
 | 
			
		||||
	Id                 string `json:"id"`
 | 
			
		||||
	Version            string `json:"version"`
 | 
			
		||||
	ConfigHash         string `json:"config_hash"`
 | 
			
		||||
	InterNodeUrl       string `json:"internode_url"`
 | 
			
		||||
	Hostname           string `json:"hostname"`
 | 
			
		||||
	LastSuccessfulPing int64  `json:"last_ping"`
 | 
			
		||||
	IsAlive            bool   `json:"is_alive"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *ClusterInfo) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(me)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ClusterInfoFromJson(data io.Reader) *ClusterInfo {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var me ClusterInfo
 | 
			
		||||
	err := decoder.Decode(&me)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &me
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *ClusterInfo) HaveEstablishedInitialContact() bool {
 | 
			
		||||
	if me.Id != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ClusterInfosToJson(objmap []*ClusterInfo) string {
 | 
			
		||||
	if b, err := json.Marshal(objmap); err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ClusterInfosFromJson(data io.Reader) []*ClusterInfo {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
 | 
			
		||||
	var objmap []*ClusterInfo
 | 
			
		||||
	if err := decoder.Decode(&objmap); err != nil {
 | 
			
		||||
		return make([]*ClusterInfo, 0)
 | 
			
		||||
	} else {
 | 
			
		||||
		return objmap
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/mattermost/platform/model/command.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/mattermost/platform/model/command.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,11 +6,14 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	COMMAND_METHOD_POST = "P"
 | 
			
		||||
	COMMAND_METHOD_GET  = "G"
 | 
			
		||||
	MIN_TRIGGER_LENGTH  = 1
 | 
			
		||||
	MAX_TRIGGER_LENGTH  = 128
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Command struct {
 | 
			
		||||
@@ -99,7 +102,7 @@ func (o *Command) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Command.IsValid", "model.command.is_valid.team_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.Trigger) == 0 || len(o.Trigger) > 128 {
 | 
			
		||||
	if len(o.Trigger) < MIN_TRIGGER_LENGTH || len(o.Trigger) > MAX_TRIGGER_LENGTH || strings.Index(o.Trigger, "/") == 0 || strings.Contains(o.Trigger, " ") {
 | 
			
		||||
		return NewLocAppError("Command.IsValid", "model.command.is_valid.trigger.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								vendor/github.com/mattermost/platform/model/compliance.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/github.com/mattermost/platform/model/compliance.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	COMPLIANCE_STATUS_CREATED  = "created"
 | 
			
		||||
	COMPLIANCE_STATUS_RUNNING  = "running"
 | 
			
		||||
	COMPLIANCE_STATUS_FINISHED = "finished"
 | 
			
		||||
	COMPLIANCE_STATUS_FAILED   = "failed"
 | 
			
		||||
	COMPLIANCE_STATUS_REMOVED  = "removed"
 | 
			
		||||
 | 
			
		||||
	COMPLIANCE_TYPE_DAILY = "daily"
 | 
			
		||||
	COMPLIANCE_TYPE_ADHOC = "adhoc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Compliance struct {
 | 
			
		||||
	Id       string `json:"id"`
 | 
			
		||||
	CreateAt int64  `json:"create_at"`
 | 
			
		||||
	UserId   string `json:"user_id"`
 | 
			
		||||
	Status   string `json:"status"`
 | 
			
		||||
	Count    int    `json:"count"`
 | 
			
		||||
	Desc     string `json:"desc"`
 | 
			
		||||
	Type     string `json:"type"`
 | 
			
		||||
	StartAt  int64  `json:"start_at"`
 | 
			
		||||
	EndAt    int64  `json:"end_at"`
 | 
			
		||||
	Keywords string `json:"keywords"`
 | 
			
		||||
	Emails   string `json:"emails"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Compliances []Compliance
 | 
			
		||||
 | 
			
		||||
func (o *Compliance) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *Compliance) PreSave() {
 | 
			
		||||
	if me.Id == "" {
 | 
			
		||||
		me.Id = NewId()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if me.Status == "" {
 | 
			
		||||
		me.Status = COMPLIANCE_STATUS_CREATED
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	me.Count = 0
 | 
			
		||||
	me.Emails = strings.ToLower(me.Emails)
 | 
			
		||||
	me.Keywords = strings.ToLower(me.Keywords)
 | 
			
		||||
 | 
			
		||||
	me.CreateAt = GetMillis()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *Compliance) JobName() string {
 | 
			
		||||
	jobName := me.Type
 | 
			
		||||
	if me.Type == COMPLIANCE_TYPE_DAILY {
 | 
			
		||||
		jobName += "-" + me.Desc
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jobName += "-" + me.Id
 | 
			
		||||
 | 
			
		||||
	return jobName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *Compliance) IsValid() *AppError {
 | 
			
		||||
 | 
			
		||||
	if len(me.Id) != 26 {
 | 
			
		||||
		return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if me.CreateAt == 0 {
 | 
			
		||||
		return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.create_at.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(me.Desc) > 512 || len(me.Desc) == 0 {
 | 
			
		||||
		return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.desc.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if me.StartAt == 0 {
 | 
			
		||||
		return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.start_at.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if me.EndAt == 0 {
 | 
			
		||||
		return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.end_at.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if me.EndAt <= me.StartAt {
 | 
			
		||||
		return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.start_end_at.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ComplianceFromJson(data io.Reader) *Compliance {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o Compliance
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Compliances) ToJson() string {
 | 
			
		||||
	if b, err := json.Marshal(o); err != nil {
 | 
			
		||||
		return "[]"
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CompliancesFromJson(data io.Reader) Compliances {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o Compliances
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								vendor/github.com/mattermost/platform/model/compliance_post.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								vendor/github.com/mattermost/platform/model/compliance_post.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type CompliancePost struct {
 | 
			
		||||
 | 
			
		||||
	// From Team
 | 
			
		||||
	TeamName        string
 | 
			
		||||
	TeamDisplayName string
 | 
			
		||||
 | 
			
		||||
	// From Channel
 | 
			
		||||
	ChannelName        string
 | 
			
		||||
	ChannelDisplayName string
 | 
			
		||||
 | 
			
		||||
	// From User
 | 
			
		||||
	UserUsername string
 | 
			
		||||
	UserEmail    string
 | 
			
		||||
	UserNickname string
 | 
			
		||||
 | 
			
		||||
	// From Post
 | 
			
		||||
	PostId         string
 | 
			
		||||
	PostCreateAt   int64
 | 
			
		||||
	PostUpdateAt   int64
 | 
			
		||||
	PostDeleteAt   int64
 | 
			
		||||
	PostRootId     string
 | 
			
		||||
	PostParentId   string
 | 
			
		||||
	PostOriginalId string
 | 
			
		||||
	PostMessage    string
 | 
			
		||||
	PostType       string
 | 
			
		||||
	PostProps      string
 | 
			
		||||
	PostHashtags   string
 | 
			
		||||
	PostFilenames  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CompliancePostHeader() []string {
 | 
			
		||||
	return []string{
 | 
			
		||||
		"TeamName",
 | 
			
		||||
		"TeamDisplayName",
 | 
			
		||||
 | 
			
		||||
		"ChannelName",
 | 
			
		||||
		"ChannelDisplayName",
 | 
			
		||||
 | 
			
		||||
		"UserUsername",
 | 
			
		||||
		"UserEmail",
 | 
			
		||||
		"UserNickname",
 | 
			
		||||
 | 
			
		||||
		"PostId",
 | 
			
		||||
		"PostCreateAt",
 | 
			
		||||
		"PostUpdateAt",
 | 
			
		||||
		"PostDeleteAt",
 | 
			
		||||
		"PostRootId",
 | 
			
		||||
		"PostParentId",
 | 
			
		||||
		"PostOriginalId",
 | 
			
		||||
		"PostMessage",
 | 
			
		||||
		"PostType",
 | 
			
		||||
		"PostProps",
 | 
			
		||||
		"PostHashtags",
 | 
			
		||||
		"PostFilenames",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *CompliancePost) Row() []string {
 | 
			
		||||
 | 
			
		||||
	postDeleteAt := ""
 | 
			
		||||
	if me.PostDeleteAt > 0 {
 | 
			
		||||
		postDeleteAt = time.Unix(0, me.PostDeleteAt*int64(1000*1000)).Format(time.RFC3339)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postUpdateAt := ""
 | 
			
		||||
	if me.PostUpdateAt != me.PostCreateAt {
 | 
			
		||||
		postUpdateAt = time.Unix(0, me.PostUpdateAt*int64(1000*1000)).Format(time.RFC3339)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return []string{
 | 
			
		||||
		me.TeamName,
 | 
			
		||||
		me.TeamDisplayName,
 | 
			
		||||
 | 
			
		||||
		me.ChannelName,
 | 
			
		||||
		me.ChannelDisplayName,
 | 
			
		||||
 | 
			
		||||
		me.UserUsername,
 | 
			
		||||
		me.UserEmail,
 | 
			
		||||
		me.UserNickname,
 | 
			
		||||
 | 
			
		||||
		me.PostId,
 | 
			
		||||
		time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339),
 | 
			
		||||
		postUpdateAt,
 | 
			
		||||
		postDeleteAt,
 | 
			
		||||
 | 
			
		||||
		me.PostRootId,
 | 
			
		||||
		me.PostParentId,
 | 
			
		||||
		me.PostOriginalId,
 | 
			
		||||
		me.PostMessage,
 | 
			
		||||
		me.PostType,
 | 
			
		||||
		me.PostProps,
 | 
			
		||||
		me.PostHashtags,
 | 
			
		||||
		me.PostFilenames,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										602
									
								
								vendor/github.com/mattermost/platform/model/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										602
									
								
								vendor/github.com/mattermost/platform/model/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,10 +6,12 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CONN_SECURITY_NONE     = ""
 | 
			
		||||
	CONN_SECURITY_PLAIN    = "PLAIN"
 | 
			
		||||
	CONN_SECURITY_TLS      = "TLS"
 | 
			
		||||
	CONN_SECURITY_STARTTLS = "STARTTLS"
 | 
			
		||||
 | 
			
		||||
@@ -19,8 +21,12 @@ const (
 | 
			
		||||
	DATABASE_DRIVER_MYSQL    = "mysql"
 | 
			
		||||
	DATABASE_DRIVER_POSTGRES = "postgres"
 | 
			
		||||
 | 
			
		||||
	SERVICE_GITLAB = "gitlab"
 | 
			
		||||
	SERVICE_GOOGLE = "google"
 | 
			
		||||
	PASSWORD_MAXIMUM_LENGTH = 64
 | 
			
		||||
	PASSWORD_MINIMUM_LENGTH = 5
 | 
			
		||||
 | 
			
		||||
	SERVICE_GITLAB    = "gitlab"
 | 
			
		||||
	SERVICE_GOOGLE    = "google"
 | 
			
		||||
	SERVICE_OFFICE365 = "office365"
 | 
			
		||||
 | 
			
		||||
	WEBSERVER_MODE_REGULAR  = "regular"
 | 
			
		||||
	WEBSERVER_MODE_GZIP     = "gzip"
 | 
			
		||||
@@ -32,10 +38,24 @@ const (
 | 
			
		||||
	DIRECT_MESSAGE_ANY  = "any"
 | 
			
		||||
	DIRECT_MESSAGE_TEAM = "team"
 | 
			
		||||
 | 
			
		||||
	PERMISSIONS_ALL          = "all"
 | 
			
		||||
	PERMISSIONS_TEAM_ADMIN   = "team_admin"
 | 
			
		||||
	PERMISSIONS_SYSTEM_ADMIN = "system_admin"
 | 
			
		||||
 | 
			
		||||
	FAKE_SETTING = "********************************"
 | 
			
		||||
 | 
			
		||||
	RESTRICT_EMOJI_CREATION_ALL          = "all"
 | 
			
		||||
	RESTRICT_EMOJI_CREATION_ADMIN        = "admin"
 | 
			
		||||
	RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin"
 | 
			
		||||
 | 
			
		||||
	EMAIL_BATCHING_BUFFER_SIZE = 256
 | 
			
		||||
	EMAIL_BATCHING_INTERVAL    = 30
 | 
			
		||||
 | 
			
		||||
	SITENAME_MAX_LENGTH = 30
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ServiceSettings struct {
 | 
			
		||||
	SiteURL                           *string
 | 
			
		||||
	ListenAddress                     string
 | 
			
		||||
	MaximumLoginAttempts              int
 | 
			
		||||
	SegmentDeveloperKey               string
 | 
			
		||||
@@ -60,6 +80,14 @@ type ServiceSettings struct {
 | 
			
		||||
	WebsocketSecurePort               *int
 | 
			
		||||
	WebsocketPort                     *int
 | 
			
		||||
	WebserverMode                     *string
 | 
			
		||||
	EnableCustomEmoji                 *bool
 | 
			
		||||
	RestrictCustomEmojiCreation       *string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ClusterSettings struct {
 | 
			
		||||
	Enable                 *bool
 | 
			
		||||
	InterNodeListenAddress *string
 | 
			
		||||
	InterNodeUrls          []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SSOSettings struct {
 | 
			
		||||
@@ -83,19 +111,30 @@ type SqlSettings struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LogSettings struct {
 | 
			
		||||
	EnableConsole bool
 | 
			
		||||
	ConsoleLevel  string
 | 
			
		||||
	EnableFile    bool
 | 
			
		||||
	FileLevel     string
 | 
			
		||||
	FileFormat    string
 | 
			
		||||
	FileLocation  string
 | 
			
		||||
	EnableConsole          bool
 | 
			
		||||
	ConsoleLevel           string
 | 
			
		||||
	EnableFile             bool
 | 
			
		||||
	FileLevel              string
 | 
			
		||||
	FileFormat             string
 | 
			
		||||
	FileLocation           string
 | 
			
		||||
	EnableWebhookDebugging bool
 | 
			
		||||
	EnableDiagnostics      *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PasswordSettings struct {
 | 
			
		||||
	MinimumLength *int
 | 
			
		||||
	Lowercase     *bool
 | 
			
		||||
	Number        *bool
 | 
			
		||||
	Uppercase     *bool
 | 
			
		||||
	Symbol        *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FileSettings struct {
 | 
			
		||||
	MaxFileSize                *int64
 | 
			
		||||
	DriverName                 string
 | 
			
		||||
	Directory                  string
 | 
			
		||||
	EnablePublicLink           bool
 | 
			
		||||
	PublicLinkSalt             string
 | 
			
		||||
	PublicLinkSalt             *string
 | 
			
		||||
	ThumbnailWidth             int
 | 
			
		||||
	ThumbnailHeight            int
 | 
			
		||||
	PreviewWidth               int
 | 
			
		||||
@@ -121,6 +160,7 @@ type EmailSettings struct {
 | 
			
		||||
	RequireEmailVerification bool
 | 
			
		||||
	FeedbackName             string
 | 
			
		||||
	FeedbackEmail            string
 | 
			
		||||
	FeedbackOrganization     *string
 | 
			
		||||
	SMTPUsername             string
 | 
			
		||||
	SMTPPassword             string
 | 
			
		||||
	SMTPServer               string
 | 
			
		||||
@@ -131,6 +171,9 @@ type EmailSettings struct {
 | 
			
		||||
	SendPushNotifications    *bool
 | 
			
		||||
	PushNotificationServer   *string
 | 
			
		||||
	PushNotificationContents *string
 | 
			
		||||
	EnableEmailBatching      *bool
 | 
			
		||||
	EmailBatchingBufferSize  *int
 | 
			
		||||
	EmailBatchingInterval    *int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RateLimitSettings struct {
 | 
			
		||||
@@ -156,16 +199,21 @@ type SupportSettings struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TeamSettings struct {
 | 
			
		||||
	SiteName                  string
 | 
			
		||||
	MaxUsersPerTeam           int
 | 
			
		||||
	EnableTeamCreation        bool
 | 
			
		||||
	EnableUserCreation        bool
 | 
			
		||||
	EnableOpenServer          *bool
 | 
			
		||||
	RestrictCreationToDomains string
 | 
			
		||||
	RestrictTeamNames         *bool
 | 
			
		||||
	EnableCustomBrand         *bool
 | 
			
		||||
	CustomBrandText           *string
 | 
			
		||||
	RestrictDirectMessage     *string
 | 
			
		||||
	SiteName                         string
 | 
			
		||||
	MaxUsersPerTeam                  int
 | 
			
		||||
	EnableTeamCreation               bool
 | 
			
		||||
	EnableUserCreation               bool
 | 
			
		||||
	EnableOpenServer                 *bool
 | 
			
		||||
	RestrictCreationToDomains        string
 | 
			
		||||
	RestrictTeamNames                *bool
 | 
			
		||||
	EnableCustomBrand                *bool
 | 
			
		||||
	CustomBrandText                  *string
 | 
			
		||||
	CustomDescriptionText            *string
 | 
			
		||||
	RestrictDirectMessage            *string
 | 
			
		||||
	RestrictTeamInvite               *string
 | 
			
		||||
	RestrictPublicChannelManagement  *string
 | 
			
		||||
	RestrictPrivateChannelManagement *string
 | 
			
		||||
	UserStatusAwayTimeout            *int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LdapSettings struct {
 | 
			
		||||
@@ -189,9 +237,13 @@ type LdapSettings struct {
 | 
			
		||||
	NicknameAttribute  *string
 | 
			
		||||
	IdAttribute        *string
 | 
			
		||||
 | 
			
		||||
	// Syncronization
 | 
			
		||||
	SyncIntervalMinutes *int
 | 
			
		||||
 | 
			
		||||
	// Advanced
 | 
			
		||||
	SkipCertificateVerification *bool
 | 
			
		||||
	QueryTimeout                *int
 | 
			
		||||
	MaxPageSize                 *int
 | 
			
		||||
 | 
			
		||||
	// Customization
 | 
			
		||||
	LoginFieldName *string
 | 
			
		||||
@@ -203,20 +255,63 @@ type ComplianceSettings struct {
 | 
			
		||||
	EnableDaily *bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LocalizationSettings struct {
 | 
			
		||||
	DefaultServerLocale *string
 | 
			
		||||
	DefaultClientLocale *string
 | 
			
		||||
	AvailableLocales    *string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SamlSettings struct {
 | 
			
		||||
	// Basic
 | 
			
		||||
	Enable  *bool
 | 
			
		||||
	Verify  *bool
 | 
			
		||||
	Encrypt *bool
 | 
			
		||||
 | 
			
		||||
	IdpUrl                      *string
 | 
			
		||||
	IdpDescriptorUrl            *string
 | 
			
		||||
	AssertionConsumerServiceURL *string
 | 
			
		||||
 | 
			
		||||
	IdpCertificateFile    *string
 | 
			
		||||
	PublicCertificateFile *string
 | 
			
		||||
	PrivateKeyFile        *string
 | 
			
		||||
 | 
			
		||||
	// User Mapping
 | 
			
		||||
	FirstNameAttribute *string
 | 
			
		||||
	LastNameAttribute  *string
 | 
			
		||||
	EmailAttribute     *string
 | 
			
		||||
	UsernameAttribute  *string
 | 
			
		||||
	NicknameAttribute  *string
 | 
			
		||||
	LocaleAttribute    *string
 | 
			
		||||
 | 
			
		||||
	LoginButtonText *string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NativeAppSettings struct {
 | 
			
		||||
	AppDownloadLink        *string
 | 
			
		||||
	AndroidAppDownloadLink *string
 | 
			
		||||
	IosAppDownloadLink     *string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	ServiceSettings    ServiceSettings
 | 
			
		||||
	TeamSettings       TeamSettings
 | 
			
		||||
	SqlSettings        SqlSettings
 | 
			
		||||
	LogSettings        LogSettings
 | 
			
		||||
	FileSettings       FileSettings
 | 
			
		||||
	EmailSettings      EmailSettings
 | 
			
		||||
	RateLimitSettings  RateLimitSettings
 | 
			
		||||
	PrivacySettings    PrivacySettings
 | 
			
		||||
	SupportSettings    SupportSettings
 | 
			
		||||
	GitLabSettings     SSOSettings
 | 
			
		||||
	GoogleSettings     SSOSettings
 | 
			
		||||
	LdapSettings       LdapSettings
 | 
			
		||||
	ComplianceSettings ComplianceSettings
 | 
			
		||||
	ServiceSettings      ServiceSettings
 | 
			
		||||
	TeamSettings         TeamSettings
 | 
			
		||||
	SqlSettings          SqlSettings
 | 
			
		||||
	LogSettings          LogSettings
 | 
			
		||||
	PasswordSettings     PasswordSettings
 | 
			
		||||
	FileSettings         FileSettings
 | 
			
		||||
	EmailSettings        EmailSettings
 | 
			
		||||
	RateLimitSettings    RateLimitSettings
 | 
			
		||||
	PrivacySettings      PrivacySettings
 | 
			
		||||
	SupportSettings      SupportSettings
 | 
			
		||||
	GitLabSettings       SSOSettings
 | 
			
		||||
	GoogleSettings       SSOSettings
 | 
			
		||||
	Office365Settings    SSOSettings
 | 
			
		||||
	LdapSettings         LdapSettings
 | 
			
		||||
	ComplianceSettings   ComplianceSettings
 | 
			
		||||
	LocalizationSettings LocalizationSettings
 | 
			
		||||
	SamlSettings         SamlSettings
 | 
			
		||||
	NativeAppSettings    NativeAppSettings
 | 
			
		||||
	ClusterSettings      ClusterSettings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Config) ToJson() string {
 | 
			
		||||
@@ -234,6 +329,8 @@ func (o *Config) GetSSOService(service string) *SSOSettings {
 | 
			
		||||
		return &o.GitLabSettings
 | 
			
		||||
	case SERVICE_GOOGLE:
 | 
			
		||||
		return &o.GoogleSettings
 | 
			
		||||
	case SERVICE_OFFICE365:
 | 
			
		||||
		return &o.Office365Settings
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -256,8 +353,14 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		o.SqlSettings.AtRestEncryptKey = NewRandomString(32)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.FileSettings.PublicLinkSalt) == 0 {
 | 
			
		||||
		o.FileSettings.PublicLinkSalt = NewRandomString(32)
 | 
			
		||||
	if o.FileSettings.MaxFileSize == nil {
 | 
			
		||||
		o.FileSettings.MaxFileSize = new(int64)
 | 
			
		||||
		*o.FileSettings.MaxFileSize = 52428800 // 50 MB
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*o.FileSettings.PublicLinkSalt) == 0 {
 | 
			
		||||
		o.FileSettings.PublicLinkSalt = new(string)
 | 
			
		||||
		*o.FileSettings.PublicLinkSalt = NewRandomString(32)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.FileSettings.AmazonS3LocationConstraint == nil {
 | 
			
		||||
@@ -278,6 +381,11 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		o.EmailSettings.PasswordResetSalt = NewRandomString(32)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ServiceSettings.SiteURL == nil {
 | 
			
		||||
		o.ServiceSettings.SiteURL = new(string)
 | 
			
		||||
		*o.ServiceSettings.SiteURL = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ServiceSettings.EnableDeveloper == nil {
 | 
			
		||||
		o.ServiceSettings.EnableDeveloper = new(bool)
 | 
			
		||||
		*o.ServiceSettings.EnableDeveloper = false
 | 
			
		||||
@@ -298,6 +406,31 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		*o.ServiceSettings.EnableMultifactorAuthentication = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.PasswordSettings.MinimumLength == nil {
 | 
			
		||||
		o.PasswordSettings.MinimumLength = new(int)
 | 
			
		||||
		*o.PasswordSettings.MinimumLength = PASSWORD_MINIMUM_LENGTH
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.PasswordSettings.Lowercase == nil {
 | 
			
		||||
		o.PasswordSettings.Lowercase = new(bool)
 | 
			
		||||
		*o.PasswordSettings.Lowercase = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.PasswordSettings.Number == nil {
 | 
			
		||||
		o.PasswordSettings.Number = new(bool)
 | 
			
		||||
		*o.PasswordSettings.Number = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.PasswordSettings.Uppercase == nil {
 | 
			
		||||
		o.PasswordSettings.Uppercase = new(bool)
 | 
			
		||||
		*o.PasswordSettings.Uppercase = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.PasswordSettings.Symbol == nil {
 | 
			
		||||
		o.PasswordSettings.Symbol = new(bool)
 | 
			
		||||
		*o.PasswordSettings.Symbol = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.RestrictTeamNames == nil {
 | 
			
		||||
		o.TeamSettings.RestrictTeamNames = new(bool)
 | 
			
		||||
		*o.TeamSettings.RestrictTeamNames = true
 | 
			
		||||
@@ -313,6 +446,11 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		*o.TeamSettings.CustomBrandText = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.CustomDescriptionText == nil {
 | 
			
		||||
		o.TeamSettings.CustomDescriptionText = new(string)
 | 
			
		||||
		*o.TeamSettings.CustomDescriptionText = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.EnableOpenServer == nil {
 | 
			
		||||
		o.TeamSettings.EnableOpenServer = new(bool)
 | 
			
		||||
		*o.TeamSettings.EnableOpenServer = false
 | 
			
		||||
@@ -323,6 +461,26 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		*o.TeamSettings.RestrictDirectMessage = DIRECT_MESSAGE_ANY
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.RestrictTeamInvite == nil {
 | 
			
		||||
		o.TeamSettings.RestrictTeamInvite = new(string)
 | 
			
		||||
		*o.TeamSettings.RestrictTeamInvite = PERMISSIONS_ALL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.RestrictPublicChannelManagement == nil {
 | 
			
		||||
		o.TeamSettings.RestrictPublicChannelManagement = new(string)
 | 
			
		||||
		*o.TeamSettings.RestrictPublicChannelManagement = PERMISSIONS_ALL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.RestrictPrivateChannelManagement == nil {
 | 
			
		||||
		o.TeamSettings.RestrictPrivateChannelManagement = new(string)
 | 
			
		||||
		*o.TeamSettings.RestrictPrivateChannelManagement = PERMISSIONS_ALL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.UserStatusAwayTimeout == nil {
 | 
			
		||||
		o.TeamSettings.UserStatusAwayTimeout = new(int64)
 | 
			
		||||
		*o.TeamSettings.UserStatusAwayTimeout = 300
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.EmailSettings.EnableSignInWithEmail == nil {
 | 
			
		||||
		o.EmailSettings.EnableSignInWithEmail = new(bool)
 | 
			
		||||
 | 
			
		||||
@@ -353,13 +511,33 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		*o.EmailSettings.PushNotificationContents = GENERIC_NOTIFICATION
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.EmailSettings.FeedbackOrganization == nil {
 | 
			
		||||
		o.EmailSettings.FeedbackOrganization = new(string)
 | 
			
		||||
		*o.EmailSettings.FeedbackOrganization = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.EmailSettings.EnableEmailBatching == nil {
 | 
			
		||||
		o.EmailSettings.EnableEmailBatching = new(bool)
 | 
			
		||||
		*o.EmailSettings.EnableEmailBatching = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.EmailSettings.EmailBatchingBufferSize == nil {
 | 
			
		||||
		o.EmailSettings.EmailBatchingBufferSize = new(int)
 | 
			
		||||
		*o.EmailSettings.EmailBatchingBufferSize = EMAIL_BATCHING_BUFFER_SIZE
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.EmailSettings.EmailBatchingInterval == nil {
 | 
			
		||||
		o.EmailSettings.EmailBatchingInterval = new(int)
 | 
			
		||||
		*o.EmailSettings.EmailBatchingInterval = EMAIL_BATCHING_INTERVAL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsSafeLink(o.SupportSettings.TermsOfServiceLink) {
 | 
			
		||||
		o.SupportSettings.TermsOfServiceLink = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SupportSettings.TermsOfServiceLink == nil {
 | 
			
		||||
		o.SupportSettings.TermsOfServiceLink = new(string)
 | 
			
		||||
		*o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html"
 | 
			
		||||
		*o.SupportSettings.TermsOfServiceLink = "https://about.mattermost.com/default-terms/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) {
 | 
			
		||||
@@ -368,7 +546,7 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
 | 
			
		||||
	if o.SupportSettings.PrivacyPolicyLink == nil {
 | 
			
		||||
		o.SupportSettings.PrivacyPolicyLink = new(string)
 | 
			
		||||
		*o.SupportSettings.PrivacyPolicyLink = "/static/help/privacy.html"
 | 
			
		||||
		*o.SupportSettings.PrivacyPolicyLink = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsSafeLink(o.SupportSettings.AboutLink) {
 | 
			
		||||
@@ -377,7 +555,7 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
 | 
			
		||||
	if o.SupportSettings.AboutLink == nil {
 | 
			
		||||
		o.SupportSettings.AboutLink = new(string)
 | 
			
		||||
		*o.SupportSettings.AboutLink = "/static/help/about.html"
 | 
			
		||||
		*o.SupportSettings.AboutLink = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsSafeLink(o.SupportSettings.HelpLink) {
 | 
			
		||||
@@ -386,7 +564,7 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
 | 
			
		||||
	if o.SupportSettings.HelpLink == nil {
 | 
			
		||||
		o.SupportSettings.HelpLink = new(string)
 | 
			
		||||
		*o.SupportSettings.HelpLink = "/static/help/help.html"
 | 
			
		||||
		*o.SupportSettings.HelpLink = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsSafeLink(o.SupportSettings.ReportAProblemLink) {
 | 
			
		||||
@@ -395,7 +573,7 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
 | 
			
		||||
	if o.SupportSettings.ReportAProblemLink == nil {
 | 
			
		||||
		o.SupportSettings.ReportAProblemLink = new(string)
 | 
			
		||||
		*o.SupportSettings.ReportAProblemLink = "/static/help/report_problem.html"
 | 
			
		||||
		*o.SupportSettings.ReportAProblemLink = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SupportSettings.SupportEmail == nil {
 | 
			
		||||
@@ -403,24 +581,94 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		*o.SupportSettings.SupportEmail = "feedback@mattermost.com"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.Enable == nil {
 | 
			
		||||
		o.LdapSettings.Enable = new(bool)
 | 
			
		||||
		*o.LdapSettings.Enable = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.LdapServer == nil {
 | 
			
		||||
		o.LdapSettings.LdapServer = new(string)
 | 
			
		||||
		*o.LdapSettings.LdapServer = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.LdapPort == nil {
 | 
			
		||||
		o.LdapSettings.LdapPort = new(int)
 | 
			
		||||
		*o.LdapSettings.LdapPort = 389
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.ConnectionSecurity == nil {
 | 
			
		||||
		o.LdapSettings.ConnectionSecurity = new(string)
 | 
			
		||||
		*o.LdapSettings.ConnectionSecurity = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.BaseDN == nil {
 | 
			
		||||
		o.LdapSettings.BaseDN = new(string)
 | 
			
		||||
		*o.LdapSettings.BaseDN = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.BindUsername == nil {
 | 
			
		||||
		o.LdapSettings.BindUsername = new(string)
 | 
			
		||||
		*o.LdapSettings.BindUsername = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.BindPassword == nil {
 | 
			
		||||
		o.LdapSettings.BindPassword = new(string)
 | 
			
		||||
		*o.LdapSettings.BindPassword = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.UserFilter == nil {
 | 
			
		||||
		o.LdapSettings.UserFilter = new(string)
 | 
			
		||||
		*o.LdapSettings.UserFilter = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.FirstNameAttribute == nil {
 | 
			
		||||
		o.LdapSettings.FirstNameAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.FirstNameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.LastNameAttribute == nil {
 | 
			
		||||
		o.LdapSettings.LastNameAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.LastNameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.EmailAttribute == nil {
 | 
			
		||||
		o.LdapSettings.EmailAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.EmailAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.UsernameAttribute == nil {
 | 
			
		||||
		o.LdapSettings.UsernameAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.UsernameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.NicknameAttribute == nil {
 | 
			
		||||
		o.LdapSettings.NicknameAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.NicknameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.IdAttribute == nil {
 | 
			
		||||
		o.LdapSettings.IdAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.IdAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.SyncIntervalMinutes == nil {
 | 
			
		||||
		o.LdapSettings.SyncIntervalMinutes = new(int)
 | 
			
		||||
		*o.LdapSettings.SyncIntervalMinutes = 60
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.SkipCertificateVerification == nil {
 | 
			
		||||
		o.LdapSettings.SkipCertificateVerification = new(bool)
 | 
			
		||||
		*o.LdapSettings.SkipCertificateVerification = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.QueryTimeout == nil {
 | 
			
		||||
		o.LdapSettings.QueryTimeout = new(int)
 | 
			
		||||
		*o.LdapSettings.QueryTimeout = 60
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.Enable == nil {
 | 
			
		||||
		o.LdapSettings.Enable = new(bool)
 | 
			
		||||
		*o.LdapSettings.Enable = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.UserFilter == nil {
 | 
			
		||||
		o.LdapSettings.UserFilter = new(string)
 | 
			
		||||
		*o.LdapSettings.UserFilter = ""
 | 
			
		||||
	if o.LdapSettings.MaxPageSize == nil {
 | 
			
		||||
		o.LdapSettings.MaxPageSize = new(int)
 | 
			
		||||
		*o.LdapSettings.MaxPageSize = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.LoginFieldName == nil {
 | 
			
		||||
@@ -475,7 +723,33 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
 | 
			
		||||
	if o.ServiceSettings.WebserverMode == nil {
 | 
			
		||||
		o.ServiceSettings.WebserverMode = new(string)
 | 
			
		||||
		*o.ServiceSettings.WebserverMode = "regular"
 | 
			
		||||
		*o.ServiceSettings.WebserverMode = "gzip"
 | 
			
		||||
	} else if *o.ServiceSettings.WebserverMode == "regular" {
 | 
			
		||||
		*o.ServiceSettings.WebserverMode = "gzip"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ServiceSettings.EnableCustomEmoji == nil {
 | 
			
		||||
		o.ServiceSettings.EnableCustomEmoji = new(bool)
 | 
			
		||||
		*o.ServiceSettings.EnableCustomEmoji = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ServiceSettings.RestrictCustomEmojiCreation == nil {
 | 
			
		||||
		o.ServiceSettings.RestrictCustomEmojiCreation = new(string)
 | 
			
		||||
		*o.ServiceSettings.RestrictCustomEmojiCreation = RESTRICT_EMOJI_CREATION_ALL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ClusterSettings.InterNodeListenAddress == nil {
 | 
			
		||||
		o.ClusterSettings.InterNodeListenAddress = new(string)
 | 
			
		||||
		*o.ClusterSettings.InterNodeListenAddress = ":8075"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ClusterSettings.Enable == nil {
 | 
			
		||||
		o.ClusterSettings.Enable = new(bool)
 | 
			
		||||
		*o.ClusterSettings.Enable = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ClusterSettings.InterNodeUrls == nil {
 | 
			
		||||
		o.ClusterSettings.InterNodeUrls = []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.ComplianceSettings.Enable == nil {
 | 
			
		||||
@@ -493,19 +767,119 @@ func (o *Config) SetDefaults() {
 | 
			
		||||
		*o.ComplianceSettings.EnableDaily = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.ConnectionSecurity == nil {
 | 
			
		||||
		o.LdapSettings.ConnectionSecurity = new(string)
 | 
			
		||||
		*o.LdapSettings.ConnectionSecurity = ""
 | 
			
		||||
	if o.LocalizationSettings.DefaultServerLocale == nil {
 | 
			
		||||
		o.LocalizationSettings.DefaultServerLocale = new(string)
 | 
			
		||||
		*o.LocalizationSettings.DefaultServerLocale = DEFAULT_LOCALE
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.SkipCertificateVerification == nil {
 | 
			
		||||
		o.LdapSettings.SkipCertificateVerification = new(bool)
 | 
			
		||||
		*o.LdapSettings.SkipCertificateVerification = false
 | 
			
		||||
	if o.LocalizationSettings.DefaultClientLocale == nil {
 | 
			
		||||
		o.LocalizationSettings.DefaultClientLocale = new(string)
 | 
			
		||||
		*o.LocalizationSettings.DefaultClientLocale = DEFAULT_LOCALE
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LdapSettings.NicknameAttribute == nil {
 | 
			
		||||
		o.LdapSettings.NicknameAttribute = new(string)
 | 
			
		||||
		*o.LdapSettings.NicknameAttribute = ""
 | 
			
		||||
	if o.LocalizationSettings.AvailableLocales == nil {
 | 
			
		||||
		o.LocalizationSettings.AvailableLocales = new(string)
 | 
			
		||||
		*o.LocalizationSettings.AvailableLocales = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.LogSettings.EnableDiagnostics == nil {
 | 
			
		||||
		o.LogSettings.EnableDiagnostics = new(bool)
 | 
			
		||||
		*o.LogSettings.EnableDiagnostics = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.Enable == nil {
 | 
			
		||||
		o.SamlSettings.Enable = new(bool)
 | 
			
		||||
		*o.SamlSettings.Enable = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.Verify == nil {
 | 
			
		||||
		o.SamlSettings.Verify = new(bool)
 | 
			
		||||
		*o.SamlSettings.Verify = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.Encrypt == nil {
 | 
			
		||||
		o.SamlSettings.Encrypt = new(bool)
 | 
			
		||||
		*o.SamlSettings.Encrypt = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.IdpUrl == nil {
 | 
			
		||||
		o.SamlSettings.IdpUrl = new(string)
 | 
			
		||||
		*o.SamlSettings.IdpUrl = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.IdpDescriptorUrl == nil {
 | 
			
		||||
		o.SamlSettings.IdpDescriptorUrl = new(string)
 | 
			
		||||
		*o.SamlSettings.IdpDescriptorUrl = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.IdpCertificateFile == nil {
 | 
			
		||||
		o.SamlSettings.IdpCertificateFile = new(string)
 | 
			
		||||
		*o.SamlSettings.IdpCertificateFile = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.PublicCertificateFile == nil {
 | 
			
		||||
		o.SamlSettings.PublicCertificateFile = new(string)
 | 
			
		||||
		*o.SamlSettings.PublicCertificateFile = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.PrivateKeyFile == nil {
 | 
			
		||||
		o.SamlSettings.PrivateKeyFile = new(string)
 | 
			
		||||
		*o.SamlSettings.PrivateKeyFile = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.AssertionConsumerServiceURL == nil {
 | 
			
		||||
		o.SamlSettings.AssertionConsumerServiceURL = new(string)
 | 
			
		||||
		*o.SamlSettings.AssertionConsumerServiceURL = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.LoginButtonText == nil || *o.SamlSettings.LoginButtonText == "" {
 | 
			
		||||
		o.SamlSettings.LoginButtonText = new(string)
 | 
			
		||||
		*o.SamlSettings.LoginButtonText = USER_AUTH_SERVICE_SAML_TEXT
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.FirstNameAttribute == nil {
 | 
			
		||||
		o.SamlSettings.FirstNameAttribute = new(string)
 | 
			
		||||
		*o.SamlSettings.FirstNameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.LastNameAttribute == nil {
 | 
			
		||||
		o.SamlSettings.LastNameAttribute = new(string)
 | 
			
		||||
		*o.SamlSettings.LastNameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.EmailAttribute == nil {
 | 
			
		||||
		o.SamlSettings.EmailAttribute = new(string)
 | 
			
		||||
		*o.SamlSettings.EmailAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.UsernameAttribute == nil {
 | 
			
		||||
		o.SamlSettings.UsernameAttribute = new(string)
 | 
			
		||||
		*o.SamlSettings.UsernameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.NicknameAttribute == nil {
 | 
			
		||||
		o.SamlSettings.NicknameAttribute = new(string)
 | 
			
		||||
		*o.SamlSettings.NicknameAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.SamlSettings.LocaleAttribute == nil {
 | 
			
		||||
		o.SamlSettings.LocaleAttribute = new(string)
 | 
			
		||||
		*o.SamlSettings.LocaleAttribute = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.NativeAppSettings.AppDownloadLink == nil {
 | 
			
		||||
		o.NativeAppSettings.AppDownloadLink = new(string)
 | 
			
		||||
		*o.NativeAppSettings.AppDownloadLink = "https://about.mattermost.com/downloads/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.NativeAppSettings.AndroidAppDownloadLink == nil {
 | 
			
		||||
		o.NativeAppSettings.AndroidAppDownloadLink = new(string)
 | 
			
		||||
		*o.NativeAppSettings.AndroidAppDownloadLink = "https://about.mattermost.com/mattermost-android-app/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.NativeAppSettings.IosAppDownloadLink == nil {
 | 
			
		||||
		o.NativeAppSettings.IosAppDownloadLink = new(string)
 | 
			
		||||
		*o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -515,10 +889,24 @@ func (o *Config) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.login_attempts.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*o.ServiceSettings.SiteURL) != 0 {
 | 
			
		||||
		if _, err := url.ParseRequestURI(*o.ServiceSettings.SiteURL); err != nil {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.site_url.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.ServiceSettings.ListenAddress) == 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.listen_address.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.ClusterSettings.Enable && *o.EmailSettings.EnableEmailBatching {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.cluster_email_batching.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*o.ServiceSettings.SiteURL) == 0 && *o.EmailSettings.EnableEmailBatching {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.site_url_email_batching.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TeamSettings.MaxUsersPerTeam <= 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.max_users.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
@@ -547,6 +935,10 @@ func (o *Config) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_max_conn.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.FileSettings.MaxFileSize <= 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.max_file_size.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(o.FileSettings.DriverName == IMAGE_DRIVER_LOCAL || o.FileSettings.DriverName == IMAGE_DRIVER_S3) {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.file_driver.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
@@ -575,11 +967,11 @@ func (o *Config) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.file_thumb_width.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.FileSettings.PublicLinkSalt) < 32 {
 | 
			
		||||
	if len(*o.FileSettings.PublicLinkSalt) < 32 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.file_salt.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(o.EmailSettings.ConnectionSecurity == CONN_SECURITY_NONE || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_TLS || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_STARTTLS) {
 | 
			
		||||
	if !(o.EmailSettings.ConnectionSecurity == CONN_SECURITY_NONE || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_TLS || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_STARTTLS || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_PLAIN) {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.email_security.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -591,6 +983,14 @@ func (o *Config) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.email_reset_salt.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.EmailSettings.EmailBatchingBufferSize <= 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.email_batching_buffer_size.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.EmailSettings.EmailBatchingInterval < 30 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.email_batching_interval.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.RateLimitSettings.MemoryStoreSize <= 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.rate_mem.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
@@ -603,6 +1003,86 @@ func (o *Config) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_security.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.LdapSettings.SyncIntervalMinutes <= 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_sync_interval.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.LdapSettings.MaxPageSize < 0 {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_max_page_size.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.LdapSettings.Enable {
 | 
			
		||||
		if *o.LdapSettings.LdapServer == "" {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_server", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if *o.LdapSettings.BaseDN == "" {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_basedn", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if *o.LdapSettings.EmailAttribute == "" {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_email", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if *o.LdapSettings.UsernameAttribute == "" {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_username", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if *o.LdapSettings.IdAttribute == "" {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_id", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.SamlSettings.Enable {
 | 
			
		||||
		if len(*o.SamlSettings.IdpUrl) == 0 || !IsValidHttpUrl(*o.SamlSettings.IdpUrl) {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_idp_url.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(*o.SamlSettings.IdpDescriptorUrl) == 0 || !IsValidHttpUrl(*o.SamlSettings.IdpDescriptorUrl) {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_idp_descriptor_url.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(*o.SamlSettings.IdpCertificateFile) == 0 {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_idp_cert.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(*o.SamlSettings.EmailAttribute) == 0 {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_email_attribute.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(*o.SamlSettings.UsernameAttribute) == 0 {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_username_attribute.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if *o.SamlSettings.Verify {
 | 
			
		||||
			if len(*o.SamlSettings.AssertionConsumerServiceURL) == 0 || !IsValidHttpUrl(*o.SamlSettings.AssertionConsumerServiceURL) {
 | 
			
		||||
				return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_assertion_consumer_service_url.app_error", nil, "")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if *o.SamlSettings.Encrypt {
 | 
			
		||||
			if len(*o.SamlSettings.PrivateKeyFile) == 0 {
 | 
			
		||||
				return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_private_key.app_error", nil, "")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(*o.SamlSettings.PublicCertificateFile) == 0 {
 | 
			
		||||
				return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_public_cert.app_error", nil, "")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(*o.SamlSettings.EmailAttribute) == 0 {
 | 
			
		||||
			return NewLocAppError("Config.IsValid", "model.config.is_valid.saml_email_attribute.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *o.PasswordSettings.MinimumLength < PASSWORD_MINIMUM_LENGTH || *o.PasswordSettings.MinimumLength > PASSWORD_MAXIMUM_LENGTH {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.password_length.app_error", map[string]interface{}{"MinLength": PASSWORD_MINIMUM_LENGTH, "MaxLength": PASSWORD_MAXIMUM_LENGTH}, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.TeamSettings.SiteName) > SITENAME_MAX_LENGTH {
 | 
			
		||||
		return NewLocAppError("Config.IsValid", "model.config.is_valid.sitename_length.app_error", map[string]interface{}{"MaxLength": SITENAME_MAX_LENGTH}, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -615,11 +1095,11 @@ func (o *Config) GetSanitizeOptions() map[string]bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Config) Sanitize() {
 | 
			
		||||
	if &o.LdapSettings != nil && len(*o.LdapSettings.BindPassword) > 0 {
 | 
			
		||||
	if o.LdapSettings.BindPassword != nil && len(*o.LdapSettings.BindPassword) > 0 {
 | 
			
		||||
		*o.LdapSettings.BindPassword = FAKE_SETTING
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	o.FileSettings.PublicLinkSalt = FAKE_SETTING
 | 
			
		||||
	*o.FileSettings.PublicLinkSalt = FAKE_SETTING
 | 
			
		||||
	if len(o.FileSettings.AmazonS3SecretAccessKey) > 0 {
 | 
			
		||||
		o.FileSettings.AmazonS3SecretAccessKey = FAKE_SETTING
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								vendor/github.com/mattermost/platform/model/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								vendor/github.com/mattermost/platform/model/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Emoji struct {
 | 
			
		||||
	Id        string `json:"id"`
 | 
			
		||||
	CreateAt  int64  `json:"create_at"`
 | 
			
		||||
	UpdateAt  int64  `json:"update_at"`
 | 
			
		||||
	DeleteAt  int64  `json:"delete_at"`
 | 
			
		||||
	CreatorId string `json:"creator_id"`
 | 
			
		||||
	Name      string `json:"name"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (emoji *Emoji) IsValid() *AppError {
 | 
			
		||||
	if len(emoji.Id) != 26 {
 | 
			
		||||
		return NewLocAppError("Emoji.IsValid", "model.emoji.id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if emoji.CreateAt == 0 {
 | 
			
		||||
		return NewLocAppError("Emoji.IsValid", "model.emoji.create_at.app_error", nil, "id="+emoji.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if emoji.UpdateAt == 0 {
 | 
			
		||||
		return NewLocAppError("Emoji.IsValid", "model.emoji.update_at.app_error", nil, "id="+emoji.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(emoji.CreatorId) != 26 {
 | 
			
		||||
		return NewLocAppError("Emoji.IsValid", "model.emoji.user_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(emoji.Name) == 0 || len(emoji.Name) > 64 {
 | 
			
		||||
		return NewLocAppError("Emoji.IsValid", "model.emoji.name.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (emoji *Emoji) PreSave() {
 | 
			
		||||
	if emoji.Id == "" {
 | 
			
		||||
		emoji.Id = NewId()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emoji.CreateAt = GetMillis()
 | 
			
		||||
	emoji.UpdateAt = emoji.CreateAt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (emoji *Emoji) PreUpdate() {
 | 
			
		||||
	emoji.UpdateAt = GetMillis()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (emoji *Emoji) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(emoji)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EmojiFromJson(data io.Reader) *Emoji {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var emoji Emoji
 | 
			
		||||
	err := decoder.Decode(&emoji)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &emoji
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EmojiListToJson(emojiList []*Emoji) string {
 | 
			
		||||
	b, err := json.Marshal(emojiList)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EmojiListFromJson(data io.Reader) []*Emoji {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var emojiList []*Emoji
 | 
			
		||||
	err := decoder.Decode(&emojiList)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return emojiList
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/mattermost/platform/model/file.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/mattermost/platform/model/file.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -8,10 +8,6 @@ import (
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MAX_FILE_SIZE = 50000000 // 50 MB
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"}
 | 
			
		||||
	IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/mattermost/platform/model/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/mattermost/platform/model/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	USER_AUTH_SERVICE_GITLAB = "gitlab"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/mattermost/platform/model/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -47,7 +47,8 @@ func userFromGitLabUser(glu *GitLabUser) *model.User {
 | 
			
		||||
	}
 | 
			
		||||
	strings.TrimSpace(user.Email)
 | 
			
		||||
	user.Email = glu.Email
 | 
			
		||||
	*user.AuthData = strconv.FormatInt(glu.Id, 10)
 | 
			
		||||
	userId := strconv.FormatInt(glu.Id, 10)
 | 
			
		||||
	user.AuthData = &userId
 | 
			
		||||
	user.AuthService = model.USER_AUTH_SERVICE_GITLAB
 | 
			
		||||
 | 
			
		||||
	return user
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										135
									
								
								vendor/github.com/mattermost/platform/model/incoming_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										135
									
								
								vendor/github.com/mattermost/platform/model/incoming_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -4,13 +4,15 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_WEBHOOK_USERNAME = "webhook"
 | 
			
		||||
	DEFAULT_WEBHOOK_ICON     = "/static/images/webhook_icon.jpg"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IncomingWebhook struct {
 | 
			
		||||
@@ -125,13 +127,136 @@ func (o *IncomingWebhook) PreUpdate() {
 | 
			
		||||
	o.UpdateAt = GetMillis()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
// escapeControlCharsFromPayload escapes control chars (\n, \t) from a byte slice.
 | 
			
		||||
// Context:
 | 
			
		||||
// JSON strings are not supposed to contain control characters such as \n, \t,
 | 
			
		||||
// ... but some incoming webhooks might still send invalid JSON and we want to
 | 
			
		||||
// try to handle that. An example invalid JSON string from an incoming webhook
 | 
			
		||||
// might look like this (strings for both "text" and "fallback" attributes are
 | 
			
		||||
// invalid JSON strings because they contain unescaped newlines and tabs):
 | 
			
		||||
//  `{
 | 
			
		||||
//    "text": "this is a test
 | 
			
		||||
//						 that contains a newline and tabs",
 | 
			
		||||
//    "attachments": [
 | 
			
		||||
//      {
 | 
			
		||||
//        "fallback": "Required plain-text summary of the attachment
 | 
			
		||||
//										that contains a newline and tabs",
 | 
			
		||||
//        "color": "#36a64f",
 | 
			
		||||
//  			...
 | 
			
		||||
//        "text": "Optional text that appears within the attachment
 | 
			
		||||
//								 that contains a newline and tabs",
 | 
			
		||||
//  			...
 | 
			
		||||
//        "thumb_url": "http://example.com/path/to/thumb.png"
 | 
			
		||||
//      }
 | 
			
		||||
//    ]
 | 
			
		||||
//  }`
 | 
			
		||||
// This function will search for `"key": "value"` pairs, and escape \n, \t
 | 
			
		||||
// from the value.
 | 
			
		||||
func escapeControlCharsFromPayload(by []byte) []byte {
 | 
			
		||||
	// we'll search for `"text": "..."` or `"fallback": "..."`, ...
 | 
			
		||||
	keys := "text|fallback|pretext|author_name|title|value"
 | 
			
		||||
 | 
			
		||||
	// the regexp reads like this:
 | 
			
		||||
	// (?s): this flag let . match \n (default is false)
 | 
			
		||||
	// "(keys)": we search for the keys defined above
 | 
			
		||||
	// \s*:\s*: followed by 0..n spaces/tabs, a colon then 0..n spaces/tabs
 | 
			
		||||
	// ": a double-quote
 | 
			
		||||
	// (\\"|[^"])*: any number of times the `\"` string or any char but a double-quote
 | 
			
		||||
	// ": a double-quote
 | 
			
		||||
	r := `(?s)"(` + keys + `)"\s*:\s*"(\\"|[^"])*"`
 | 
			
		||||
	re := regexp.MustCompile(r)
 | 
			
		||||
 | 
			
		||||
	// the function that will escape \n and \t on the regexp matches
 | 
			
		||||
	repl := func(b []byte) []byte {
 | 
			
		||||
		if bytes.Contains(b, []byte("\n")) {
 | 
			
		||||
			b = bytes.Replace(b, []byte("\n"), []byte("\\n"), -1)
 | 
			
		||||
		}
 | 
			
		||||
		if bytes.Contains(b, []byte("\t")) {
 | 
			
		||||
			b = bytes.Replace(b, []byte("\t"), []byte("\\t"), -1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return b
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return re.ReplaceAllFunc(by, repl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decodeIncomingWebhookRequest(by []byte) (*IncomingWebhookRequest, error) {
 | 
			
		||||
	decoder := json.NewDecoder(bytes.NewReader(by))
 | 
			
		||||
	var o IncomingWebhookRequest
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
		return &o, nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// To mention @channel via a webhook in Slack, the message should contain
 | 
			
		||||
// <!channel>, as explained at the bottom of this article:
 | 
			
		||||
// https://get.slack.help/hc/en-us/articles/202009646-Making-announcements
 | 
			
		||||
func expandAnnouncement(text string) string {
 | 
			
		||||
	c1 := "<!channel>"
 | 
			
		||||
	c2 := "@channel"
 | 
			
		||||
	if strings.Contains(text, c1) {
 | 
			
		||||
		return strings.Replace(text, c1, c2, -1)
 | 
			
		||||
	}
 | 
			
		||||
	return text
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Expand announcements in incoming webhooks from Slack. Those announcements
 | 
			
		||||
// can be found in the text attribute, or in the pretext, text, title and value
 | 
			
		||||
// attributes of the attachment structure. The Slack attachment structure is
 | 
			
		||||
// documented here: https://api.slack.com/docs/attachments
 | 
			
		||||
func expandAnnouncements(i *IncomingWebhookRequest) {
 | 
			
		||||
	i.Text = expandAnnouncement(i.Text)
 | 
			
		||||
 | 
			
		||||
	if i.Attachments != nil {
 | 
			
		||||
		attachments := i.Attachments.([]interface{})
 | 
			
		||||
		for _, attachment := range attachments {
 | 
			
		||||
			a := attachment.(map[string]interface{})
 | 
			
		||||
 | 
			
		||||
			if a["pretext"] != nil {
 | 
			
		||||
				a["pretext"] = expandAnnouncement(a["pretext"].(string))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if a["text"] != nil {
 | 
			
		||||
				a["text"] = expandAnnouncement(a["text"].(string))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if a["title"] != nil {
 | 
			
		||||
				a["title"] = expandAnnouncement(a["title"].(string))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if a["fields"] != nil {
 | 
			
		||||
				fields := a["fields"].([]interface{})
 | 
			
		||||
				for _, field := range fields {
 | 
			
		||||
					f := field.(map[string]interface{})
 | 
			
		||||
					if f["value"] != nil {
 | 
			
		||||
						f["value"] = expandAnnouncement(f["value"].(string))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest {
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	buf.ReadFrom(data)
 | 
			
		||||
	by := buf.Bytes()
 | 
			
		||||
 | 
			
		||||
	// Try to decode the JSON data. Only if it fails, try to escape control
 | 
			
		||||
	// characters from the strings contained in the JSON data.
 | 
			
		||||
	o, err := decodeIncomingWebhookRequest(by)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o, err = decodeIncomingWebhookRequest(escapeControlCharsFromPayload(by))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expandAnnouncements(o)
 | 
			
		||||
 | 
			
		||||
	return o
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								vendor/github.com/mattermost/platform/model/initial_load.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/mattermost/platform/model/initial_load.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type InitialLoad struct {
 | 
			
		||||
	User           *User             `json:"user"`
 | 
			
		||||
	TeamMembers    []*TeamMember     `json:"team_members"`
 | 
			
		||||
	Teams          []*Team           `json:"teams"`
 | 
			
		||||
	DirectProfiles map[string]*User  `json:"direct_profiles"`
 | 
			
		||||
	Preferences    Preferences       `json:"preferences"`
 | 
			
		||||
	ClientCfg      map[string]string `json:"client_cfg"`
 | 
			
		||||
	LicenseCfg     map[string]string `json:"license_cfg"`
 | 
			
		||||
	NoAccounts     bool              `json:"no_accounts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *InitialLoad) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(me)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InitialLoadFromJson(data io.Reader) *InitialLoad {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o InitialLoad
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										100
									
								
								vendor/github.com/mattermost/platform/model/job.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/github.com/mattermost/platform/model/job.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TaskFunc func()
 | 
			
		||||
 | 
			
		||||
type ScheduledTask struct {
 | 
			
		||||
	Name      string        `json:"name"`
 | 
			
		||||
	Interval  time.Duration `json:"interval"`
 | 
			
		||||
	Recurring bool          `json:"recurring"`
 | 
			
		||||
	function  TaskFunc      `json:",omitempty"`
 | 
			
		||||
	timer     *time.Timer   `json:",omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tasks = make(map[string]*ScheduledTask)
 | 
			
		||||
 | 
			
		||||
func addTask(task *ScheduledTask) {
 | 
			
		||||
	tasks[task.Name] = task
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func removeTaskByName(name string) {
 | 
			
		||||
	delete(tasks, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetTaskByName(name string) *ScheduledTask {
 | 
			
		||||
	if task, ok := tasks[name]; ok {
 | 
			
		||||
		return task
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetAllTasks() *map[string]*ScheduledTask {
 | 
			
		||||
	return &tasks
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask {
 | 
			
		||||
	task := &ScheduledTask{
 | 
			
		||||
		Name:      name,
 | 
			
		||||
		Interval:  timeToExecution,
 | 
			
		||||
		Recurring: false,
 | 
			
		||||
		function:  function,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	taskRunner := func() {
 | 
			
		||||
		go task.function()
 | 
			
		||||
		removeTaskByName(task.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	task.timer = time.AfterFunc(timeToExecution, taskRunner)
 | 
			
		||||
 | 
			
		||||
	addTask(task)
 | 
			
		||||
 | 
			
		||||
	return task
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
 | 
			
		||||
	task := &ScheduledTask{
 | 
			
		||||
		Name:      name,
 | 
			
		||||
		Interval:  interval,
 | 
			
		||||
		Recurring: true,
 | 
			
		||||
		function:  function,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	taskRecurer := func() {
 | 
			
		||||
		go task.function()
 | 
			
		||||
		task.timer.Reset(task.Interval)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	task.timer = time.AfterFunc(interval, taskRecurer)
 | 
			
		||||
 | 
			
		||||
	addTask(task)
 | 
			
		||||
 | 
			
		||||
	return task
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (task *ScheduledTask) Cancel() {
 | 
			
		||||
	task.timer.Stop()
 | 
			
		||||
	removeTaskByName(task.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Executes the task immediatly. A recurring task will be run regularally after interval.
 | 
			
		||||
func (task *ScheduledTask) Execute() {
 | 
			
		||||
	task.function()
 | 
			
		||||
	task.timer.Reset(task.Interval)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (task *ScheduledTask) String() string {
 | 
			
		||||
	return fmt.Sprintf(
 | 
			
		||||
		"%s\nInterval: %s\nRecurring: %t\n",
 | 
			
		||||
		task.Name,
 | 
			
		||||
		task.Interval.String(),
 | 
			
		||||
		task.Recurring,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/mattermost/platform/model/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/mattermost/platform/model/ldap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	USER_AUTH_SERVICE_LDAP = "ldap"
 | 
			
		||||
	LDAP_SYNC_TASK_NAME    = "LDAP Syncronization"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										62
									
								
								vendor/github.com/mattermost/platform/model/license.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/mattermost/platform/model/license.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -32,14 +32,34 @@ type Customer struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Features struct {
 | 
			
		||||
	Users          *int  `json:"users"`
 | 
			
		||||
	LDAP           *bool `json:"ldap"`
 | 
			
		||||
	MFA            *bool `json:"mfa"`
 | 
			
		||||
	GoogleSSO      *bool `json:"google_sso"`
 | 
			
		||||
	Compliance     *bool `json:"compliance"`
 | 
			
		||||
	CustomBrand    *bool `json:"custom_brand"`
 | 
			
		||||
	MHPNS          *bool `json:"mhpns"`
 | 
			
		||||
	FutureFeatures *bool `json:"future_features"`
 | 
			
		||||
	Users                *int  `json:"users"`
 | 
			
		||||
	LDAP                 *bool `json:"ldap"`
 | 
			
		||||
	MFA                  *bool `json:"mfa"`
 | 
			
		||||
	GoogleOAuth          *bool `json:"google_oauth"`
 | 
			
		||||
	Office365OAuth       *bool `json:"office365_oauth"`
 | 
			
		||||
	Compliance           *bool `json:"compliance"`
 | 
			
		||||
	Cluster              *bool `json:"cluster"`
 | 
			
		||||
	CustomBrand          *bool `json:"custom_brand"`
 | 
			
		||||
	MHPNS                *bool `json:"mhpns"`
 | 
			
		||||
	SAML                 *bool `json:"saml"`
 | 
			
		||||
	PasswordRequirements *bool `json:"password_requirements"`
 | 
			
		||||
	FutureFeatures       *bool `json:"future_features"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *Features) ToMap() map[string]interface{} {
 | 
			
		||||
	return map[string]interface{}{
 | 
			
		||||
		"ldap":         *f.LDAP,
 | 
			
		||||
		"mfa":          *f.MFA,
 | 
			
		||||
		"google":       *f.GoogleOAuth,
 | 
			
		||||
		"office365":    *f.Office365OAuth,
 | 
			
		||||
		"compliance":   *f.Compliance,
 | 
			
		||||
		"cluster":      *f.Cluster,
 | 
			
		||||
		"custom_brand": *f.CustomBrand,
 | 
			
		||||
		"mhpns":        *f.MHPNS,
 | 
			
		||||
		"saml":         *f.SAML,
 | 
			
		||||
		"password":     *f.PasswordRequirements,
 | 
			
		||||
		"future":       *f.FutureFeatures,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *Features) SetDefaults() {
 | 
			
		||||
@@ -63,9 +83,14 @@ func (f *Features) SetDefaults() {
 | 
			
		||||
		*f.MFA = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.GoogleSSO == nil {
 | 
			
		||||
		f.GoogleSSO = new(bool)
 | 
			
		||||
		*f.GoogleSSO = *f.FutureFeatures
 | 
			
		||||
	if f.GoogleOAuth == nil {
 | 
			
		||||
		f.GoogleOAuth = new(bool)
 | 
			
		||||
		*f.GoogleOAuth = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.Office365OAuth == nil {
 | 
			
		||||
		f.Office365OAuth = new(bool)
 | 
			
		||||
		*f.Office365OAuth = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.Compliance == nil {
 | 
			
		||||
@@ -73,6 +98,11 @@ func (f *Features) SetDefaults() {
 | 
			
		||||
		*f.Compliance = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.Cluster == nil {
 | 
			
		||||
		f.Cluster = new(bool)
 | 
			
		||||
		*f.Cluster = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.CustomBrand == nil {
 | 
			
		||||
		f.CustomBrand = new(bool)
 | 
			
		||||
		*f.CustomBrand = *f.FutureFeatures
 | 
			
		||||
@@ -82,6 +112,16 @@ func (f *Features) SetDefaults() {
 | 
			
		||||
		f.MHPNS = new(bool)
 | 
			
		||||
		*f.MHPNS = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.SAML == nil {
 | 
			
		||||
		f.SAML = new(bool)
 | 
			
		||||
		*f.SAML = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.PasswordRequirements == nil {
 | 
			
		||||
		f.PasswordRequirements = new(bool)
 | 
			
		||||
		*f.PasswordRequirements = *f.FutureFeatures
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *License) IsExpired() bool {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								vendor/github.com/mattermost/platform/model/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/mattermost/platform/model/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,60 +0,0 @@
 | 
			
		||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ACTION_TYPING             = "typing"
 | 
			
		||||
	ACTION_POSTED             = "posted"
 | 
			
		||||
	ACTION_POST_EDITED        = "post_edited"
 | 
			
		||||
	ACTION_POST_DELETED       = "post_deleted"
 | 
			
		||||
	ACTION_CHANNEL_DELETED    = "channel_deleted"
 | 
			
		||||
	ACTION_CHANNEL_VIEWED     = "channel_viewed"
 | 
			
		||||
	ACTION_DIRECT_ADDED       = "direct_added"
 | 
			
		||||
	ACTION_NEW_USER           = "new_user"
 | 
			
		||||
	ACTION_USER_ADDED         = "user_added"
 | 
			
		||||
	ACTION_USER_REMOVED       = "user_removed"
 | 
			
		||||
	ACTION_PREFERENCE_CHANGED = "preference_changed"
 | 
			
		||||
	ACTION_EPHEMERAL_MESSAGE  = "ephemeral_message"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
	TeamId    string            `json:"team_id"`
 | 
			
		||||
	ChannelId string            `json:"channel_id"`
 | 
			
		||||
	UserId    string            `json:"user_id"`
 | 
			
		||||
	Action    string            `json:"action"`
 | 
			
		||||
	Props     map[string]string `json:"props"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Message) Add(key string, value string) {
 | 
			
		||||
	m.Props[key] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMessage(teamId string, channelId string, userId string, action string) *Message {
 | 
			
		||||
	return &Message{TeamId: teamId, ChannelId: channelId, UserId: userId, Action: action, Props: make(map[string]string)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Message) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func MessageFromJson(data io.Reader) *Message {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o Message
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								vendor/github.com/mattermost/platform/model/oauth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/mattermost/platform/model/oauth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -25,8 +25,10 @@ type OAuthApp struct {
 | 
			
		||||
	ClientSecret string      `json:"client_secret"`
 | 
			
		||||
	Name         string      `json:"name"`
 | 
			
		||||
	Description  string      `json:"description"`
 | 
			
		||||
	IconURL      string      `json:"icon_url"`
 | 
			
		||||
	CallbackUrls StringArray `json:"callback_urls"`
 | 
			
		||||
	Homepage     string      `json:"homepage"`
 | 
			
		||||
	IsTrusted    bool        `json:"is_trusted"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValid validates the app and returns an error if it isn't configured
 | 
			
		||||
@@ -61,7 +63,13 @@ func (a *OAuthApp) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(a.Homepage) == 0 || len(a.Homepage) > 256 {
 | 
			
		||||
	for _, callback := range a.CallbackUrls {
 | 
			
		||||
		if !IsValidHttpUrl(callback) {
 | 
			
		||||
			return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(a.Homepage) == 0 || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) {
 | 
			
		||||
		return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +77,12 @@ func (a *OAuthApp) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(a.IconURL) > 0 {
 | 
			
		||||
		if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) {
 | 
			
		||||
			return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -85,10 +99,6 @@ func (a *OAuthApp) PreSave() {
 | 
			
		||||
 | 
			
		||||
	a.CreateAt = GetMillis()
 | 
			
		||||
	a.UpdateAt = a.CreateAt
 | 
			
		||||
 | 
			
		||||
	if len(a.ClientSecret) > 0 {
 | 
			
		||||
		a.ClientSecret = HashPassword(a.ClientSecret)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PreUpdate should be run before updating the app in the db.
 | 
			
		||||
@@ -157,3 +167,23 @@ func OAuthAppMapFromJson(data io.Reader) map[string]*OAuthApp {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func OAuthAppListToJson(l []*OAuthApp) string {
 | 
			
		||||
	b, err := json.Marshal(l)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func OAuthAppListFromJson(data io.Reader) []*OAuthApp {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o []*OAuthApp
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								vendor/github.com/mattermost/platform/model/outgoing_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/mattermost/platform/model/outgoing_webhook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,6 +7,9 @@ import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type OutgoingWebhook struct {
 | 
			
		||||
@@ -19,9 +22,51 @@ type OutgoingWebhook struct {
 | 
			
		||||
	ChannelId    string      `json:"channel_id"`
 | 
			
		||||
	TeamId       string      `json:"team_id"`
 | 
			
		||||
	TriggerWords StringArray `json:"trigger_words"`
 | 
			
		||||
	TriggerWhen  int         `json:"trigger_when"`
 | 
			
		||||
	CallbackURLs StringArray `json:"callback_urls"`
 | 
			
		||||
	DisplayName  string      `json:"display_name"`
 | 
			
		||||
	Description  string      `json:"description"`
 | 
			
		||||
	ContentType  string      `json:"content_type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OutgoingWebhookPayload struct {
 | 
			
		||||
	Token       string `json:"token"`
 | 
			
		||||
	TeamId      string `json:"team_id"`
 | 
			
		||||
	TeamDomain  string `json:"team_domain"`
 | 
			
		||||
	ChannelId   string `json:"channel_id"`
 | 
			
		||||
	ChannelName string `json:"channel_name"`
 | 
			
		||||
	Timestamp   int64  `json:"timestamp"`
 | 
			
		||||
	UserId      string `json:"user_id"`
 | 
			
		||||
	UserName    string `json:"user_name"`
 | 
			
		||||
	PostId      string `json:"post_id"`
 | 
			
		||||
	Text        string `json:"text"`
 | 
			
		||||
	TriggerWord string `json:"trigger_word"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OutgoingWebhookPayload) ToJSON() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OutgoingWebhookPayload) ToFormValues() string {
 | 
			
		||||
	v := url.Values{}
 | 
			
		||||
	v.Set("token", o.Token)
 | 
			
		||||
	v.Set("team_id", o.TeamId)
 | 
			
		||||
	v.Set("team_domain", o.TeamDomain)
 | 
			
		||||
	v.Set("channel_id", o.ChannelId)
 | 
			
		||||
	v.Set("channel_name", o.ChannelName)
 | 
			
		||||
	v.Set("timestamp", strconv.FormatInt(o.Timestamp/1000, 10))
 | 
			
		||||
	v.Set("user_id", o.UserId)
 | 
			
		||||
	v.Set("user_name", o.UserName)
 | 
			
		||||
	v.Set("post_id", o.PostId)
 | 
			
		||||
	v.Set("text", o.Text)
 | 
			
		||||
	v.Set("trigger_word", o.TriggerWord)
 | 
			
		||||
 | 
			
		||||
	return v.Encode()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OutgoingWebhook) ToJson() string {
 | 
			
		||||
@@ -124,6 +169,14 @@ func (o *OutgoingWebhook) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.ContentType) > 128 {
 | 
			
		||||
		return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.TriggerWhen > 1 {
 | 
			
		||||
		return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -157,3 +210,17 @@ func (o *OutgoingWebhook) HasTriggerWord(word string) bool {
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool {
 | 
			
		||||
	if len(o.TriggerWords) == 0 || len(word) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, trigger := range o.TriggerWords {
 | 
			
		||||
		if strings.HasPrefix(word, trigger) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								vendor/github.com/mattermost/platform/model/password_recovery.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/mattermost/platform/model/password_recovery.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	PASSWORD_RECOVERY_CODE_SIZE  = 128
 | 
			
		||||
	PASSWORD_RECOVER_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PasswordRecovery struct {
 | 
			
		||||
	UserId   string
 | 
			
		||||
	Code     string
 | 
			
		||||
	CreateAt int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *PasswordRecovery) IsValid() *AppError {
 | 
			
		||||
 | 
			
		||||
	if len(p.UserId) != 26 {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.password_recovery.is_valid.user_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(p.Code) != PASSWORD_RECOVERY_CODE_SIZE {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.password_recovery.is_valid.code.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.CreateAt == 0 {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.password_recovery.is_valid.create_at.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *PasswordRecovery) PreSave() {
 | 
			
		||||
	p.Code = NewRandomString(PASSWORD_RECOVERY_CODE_SIZE)
 | 
			
		||||
	p.CreateAt = GetMillis()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/mattermost/platform/model/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/mattermost/platform/model/post.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,6 +15,7 @@ const (
 | 
			
		||||
	POST_SLACK_ATTACHMENT      = "slack_attachment"
 | 
			
		||||
	POST_SYSTEM_GENERIC        = "system_generic"
 | 
			
		||||
	POST_JOIN_LEAVE            = "system_join_leave"
 | 
			
		||||
	POST_ADD_REMOVE            = "system_add_remove"
 | 
			
		||||
	POST_HEADER_CHANGE         = "system_header_change"
 | 
			
		||||
	POST_CHANNEL_DELETED       = "system_channel_deleted"
 | 
			
		||||
	POST_EPHEMERAL             = "system_ephemeral"
 | 
			
		||||
@@ -109,7 +110,7 @@ func (o *Post) IsValid() *AppError {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// should be removed once more message types are supported
 | 
			
		||||
	if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE) {
 | 
			
		||||
	if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_ADD_REMOVE || o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE) {
 | 
			
		||||
		return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -162,9 +163,6 @@ func (o *Post) AddProp(key string, value interface{}) {
 | 
			
		||||
	o.Props[key] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Post) PreExport() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Post) IsSystemMessage() bool {
 | 
			
		||||
	return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								vendor/github.com/mattermost/platform/model/preference.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/mattermost/platform/model/preference.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,6 +6,8 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -13,9 +15,28 @@ const (
 | 
			
		||||
	PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
 | 
			
		||||
	PREFERENCE_CATEGORY_TUTORIAL_STEPS      = "tutorial_step"
 | 
			
		||||
	PREFERENCE_CATEGORY_ADVANCED_SETTINGS   = "advanced_settings"
 | 
			
		||||
	PREFERENCE_CATEGORY_FLAGGED_POST        = "flagged_post"
 | 
			
		||||
 | 
			
		||||
	PREFERENCE_CATEGORY_DISPLAY_SETTINGS   = "display_settings"
 | 
			
		||||
	PREFERENCE_NAME_COLLAPSE_SETTING       = "collapse_previews"
 | 
			
		||||
	PREFERENCE_NAME_DISPLAY_NAME_FORMAT    = "name_format"
 | 
			
		||||
	PREFERENCE_VALUE_DISPLAY_NAME_NICKNAME = "nickname_full_name"
 | 
			
		||||
	PREFERENCE_VALUE_DISPLAY_NAME_FULL     = "full_name"
 | 
			
		||||
	PREFERENCE_VALUE_DISPLAY_NAME_USERNAME = "username"
 | 
			
		||||
	PREFERENCE_DEFAULT_DISPLAY_NAME_FORMAT = PREFERENCE_VALUE_DISPLAY_NAME_USERNAME
 | 
			
		||||
 | 
			
		||||
	PREFERENCE_CATEGORY_THEME = "theme"
 | 
			
		||||
	// the name for theme props is the team id
 | 
			
		||||
 | 
			
		||||
	PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP = "oauth_app"
 | 
			
		||||
	// the name for oauth_app is the client_id and value is the current scope
 | 
			
		||||
 | 
			
		||||
	PREFERENCE_CATEGORY_LAST     = "last"
 | 
			
		||||
	PREFERENCE_NAME_LAST_CHANNEL = "channel"
 | 
			
		||||
 | 
			
		||||
	PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
 | 
			
		||||
	PREFERENCE_NAME_EMAIL_INTERVAL    = "email_interval"
 | 
			
		||||
	PREFERENCE_DEFAULT_EMAIL_INTERVAL = "30" // default to match the interval of the "immediate" setting (ie 30 seconds)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Preference struct {
 | 
			
		||||
@@ -54,13 +75,48 @@ func (o *Preference) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.Name) == 0 || len(o.Name) > 32 {
 | 
			
		||||
	if len(o.Name) > 32 {
 | 
			
		||||
		return NewLocAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if utf8.RuneCountInString(o.Value) > 128 {
 | 
			
		||||
	if utf8.RuneCountInString(o.Value) > 2000 {
 | 
			
		||||
		return NewLocAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.Category == PREFERENCE_CATEGORY_THEME {
 | 
			
		||||
		var unused map[string]string
 | 
			
		||||
		if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&unused); err != nil {
 | 
			
		||||
			return NewLocAppError("Preference.IsValid", "model.preference.is_valid.theme.app_error", nil, "value="+o.Value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Preference) PreUpdate() {
 | 
			
		||||
	if o.Category == PREFERENCE_CATEGORY_THEME {
 | 
			
		||||
		// decode the value of theme (a map of strings to string) and eliminate any invalid values
 | 
			
		||||
		var props map[string]string
 | 
			
		||||
		if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&props); err != nil {
 | 
			
		||||
			// just continue, the invalid preference value should get caught by IsValid before saving
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`)
 | 
			
		||||
 | 
			
		||||
		// blank out any invalid theme values
 | 
			
		||||
		for name, value := range props {
 | 
			
		||||
			if name == "image" || name == "type" || name == "codeTheme" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !colorPattern.MatchString(value) {
 | 
			
		||||
				props[name] = "#ffffff"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if b, err := json.Marshal(props); err == nil {
 | 
			
		||||
			o.Value = string(b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/github.com/mattermost/platform/model/push_notification.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/mattermost/platform/model/push_notification.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,12 +6,16 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	PUSH_NOTIFY_APPLE   = "apple"
 | 
			
		||||
	PUSH_NOTIFY_ANDROID = "android"
 | 
			
		||||
 | 
			
		||||
	PUSH_TYPE_MESSAGE = "message"
 | 
			
		||||
	PUSH_TYPE_CLEAR   = "clear"
 | 
			
		||||
 | 
			
		||||
	CATEGORY_DM = "DIRECT_MESSAGE"
 | 
			
		||||
 | 
			
		||||
	MHPNS = "https://push.mattermost.com"
 | 
			
		||||
@@ -28,6 +32,7 @@ type PushNotification struct {
 | 
			
		||||
	ContentAvailable int    `json:"cont_ava"`
 | 
			
		||||
	ChannelId        string `json:"channel_id"`
 | 
			
		||||
	ChannelName      string `json:"channel_name"`
 | 
			
		||||
	Type             string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *PushNotification) ToJson() string {
 | 
			
		||||
@@ -39,6 +44,16 @@ func (me *PushNotification) ToJson() string {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
 | 
			
		||||
	if strings.HasPrefix(deviceId, PUSH_NOTIFY_APPLE+":") {
 | 
			
		||||
		me.Platform = PUSH_NOTIFY_APPLE
 | 
			
		||||
		me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_APPLE+":")
 | 
			
		||||
	} else if strings.HasPrefix(deviceId, PUSH_NOTIFY_ANDROID+":") {
 | 
			
		||||
		me.Platform = PUSH_NOTIFY_ANDROID
 | 
			
		||||
		me.DeviceId = strings.TrimPrefix(deviceId, PUSH_NOTIFY_ANDROID+":")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PushNotificationFromJson(data io.Reader) *PushNotification {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var me PushNotification
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								vendor/github.com/mattermost/platform/model/saml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/mattermost/platform/model/saml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	USER_AUTH_SERVICE_SAML      = "saml"
 | 
			
		||||
	USER_AUTH_SERVICE_SAML_TEXT = "With SAML"
 | 
			
		||||
	SAML_IDP_CERTIFICATE        = 1
 | 
			
		||||
	SAML_PRIVATE_KEY            = 2
 | 
			
		||||
	SAML_PUBLIC_CERT            = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SamlAuthRequest struct {
 | 
			
		||||
	Base64AuthRequest string
 | 
			
		||||
	URL               string
 | 
			
		||||
	RelayState        string
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/mattermost/platform/model/search_params.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/mattermost/platform/model/search_params.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -4,9 +4,13 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var searchTermPuncStart = regexp.MustCompile(`^[^\pL\d\s#"]+`)
 | 
			
		||||
var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`)
 | 
			
		||||
 | 
			
		||||
type SearchParams struct {
 | 
			
		||||
	Terms      string
 | 
			
		||||
	IsHashtag  bool
 | 
			
		||||
@@ -91,8 +95,8 @@ func parseSearchFlags(input []string) ([]string, [][2]string) {
 | 
			
		||||
 | 
			
		||||
		if !isFlag {
 | 
			
		||||
			// trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards)
 | 
			
		||||
			word = puncStart.ReplaceAllString(word, "")
 | 
			
		||||
			word = puncEndWildcard.ReplaceAllString(word, "")
 | 
			
		||||
			word = searchTermPuncStart.ReplaceAllString(word, "")
 | 
			
		||||
			word = searchTermPuncEnd.ReplaceAllString(word, "")
 | 
			
		||||
 | 
			
		||||
			// and remove extra pound #s
 | 
			
		||||
			word = hashtagStart.ReplaceAllString(word, "#")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/mattermost/platform/model/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/mattermost/platform/model/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,6 +6,7 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -83,7 +84,11 @@ func (me *Session) IsExpired() bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *Session) SetExpireInDays(days int) {
 | 
			
		||||
	me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days))
 | 
			
		||||
	if me.CreateAt == 0 {
 | 
			
		||||
		me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days))
 | 
			
		||||
	} else {
 | 
			
		||||
		me.ExpiresAt = me.CreateAt + (1000 * 60 * 60 * 24 * int64(days))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *Session) AddProp(key string, value string) {
 | 
			
		||||
@@ -105,6 +110,11 @@ func (me *Session) GetTeamByTeamId(teamId string) *TeamMember {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (me *Session) IsMobileApp() bool {
 | 
			
		||||
	return len(me.DeviceId) > 0 &&
 | 
			
		||||
		(strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SessionsToJson(o []*Session) string {
 | 
			
		||||
	if b, err := json.Marshal(o); err != nil {
 | 
			
		||||
		return "[]"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								vendor/github.com/mattermost/platform/model/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/mattermost/platform/model/status.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	STATUS_OFFLINE         = "offline"
 | 
			
		||||
	STATUS_AWAY            = "away"
 | 
			
		||||
	STATUS_ONLINE          = "online"
 | 
			
		||||
	STATUS_CACHE_SIZE      = 10000
 | 
			
		||||
	STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Status struct {
 | 
			
		||||
	UserId         string `json:"user_id"`
 | 
			
		||||
	Status         string `json:"status"`
 | 
			
		||||
	Manual         bool   `json:"manual"`
 | 
			
		||||
	LastActivityAt int64  `json:"last_activity_at"`
 | 
			
		||||
	ActiveChannel  string `json:"active_channel"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Status) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func StatusFromJson(data io.Reader) *Status {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o Status
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/mattermost/platform/model/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/mattermost/platform/model/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -224,9 +224,6 @@ func CleanTeamName(s string) string {
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Team) PreExport() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *Team) Sanitize() {
 | 
			
		||||
	o.Email = ""
 | 
			
		||||
	o.AllowedDomains = ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								vendor/github.com/mattermost/platform/model/team_member.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								vendor/github.com/mattermost/platform/model/team_member.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ROLE_TEAM_ADMIN = "admin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TeamMember struct {
 | 
			
		||||
	TeamId   string `json:"team_id"`
 | 
			
		||||
	UserId   string `json:"user_id"`
 | 
			
		||||
	Roles    string `json:"roles"`
 | 
			
		||||
	DeleteAt int64  `json:"delete_at"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *TeamMember) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TeamMemberFromJson(data io.Reader) *TeamMember {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o TeamMember
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TeamMembersToJson(o []*TeamMember) string {
 | 
			
		||||
	if b, err := json.Marshal(o); err != nil {
 | 
			
		||||
		return "[]"
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TeamMembersFromJson(data io.Reader) []*TeamMember {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o []*TeamMember
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsValidTeamRoles(teamRoles string) bool {
 | 
			
		||||
 | 
			
		||||
	roles := strings.Split(teamRoles, " ")
 | 
			
		||||
 | 
			
		||||
	for _, r := range roles {
 | 
			
		||||
		if !isValidTeamRole(r) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidTeamRole(role string) bool {
 | 
			
		||||
	if role == "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if role == ROLE_TEAM_ADMIN {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsInTeamRole(teamRoles string, inRole string) bool {
 | 
			
		||||
	roles := strings.Split(teamRoles, " ")
 | 
			
		||||
 | 
			
		||||
	for _, r := range roles {
 | 
			
		||||
		if r == inRole {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *TeamMember) IsTeamAdmin() bool {
 | 
			
		||||
	return IsInTeamRole(o.Roles, ROLE_TEAM_ADMIN)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *TeamMember) IsValid() *AppError {
 | 
			
		||||
 | 
			
		||||
	if len(o.TeamId) != 26 {
 | 
			
		||||
		return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.team_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(o.UserId) != 26 {
 | 
			
		||||
		return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.user_id.app_error", nil, "")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, role := range strings.Split(o.Roles, " ") {
 | 
			
		||||
		if !(role == "" || role == ROLE_TEAM_ADMIN) {
 | 
			
		||||
			return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.role.app_error", nil, "role="+role)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								vendor/github.com/mattermost/platform/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/mattermost/platform/model/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,18 +16,12 @@ import (
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ROLE_SYSTEM_ADMIN          = "system_admin"
 | 
			
		||||
	USER_AWAY_TIMEOUT          = 5 * 60 * 1000 // 5 minutes
 | 
			
		||||
	USER_OFFLINE_TIMEOUT       = 1 * 60 * 1000 // 1 minute
 | 
			
		||||
	USER_OFFLINE               = "offline"
 | 
			
		||||
	USER_AWAY                  = "away"
 | 
			
		||||
	USER_ONLINE                = "online"
 | 
			
		||||
	USER_NOTIFY_ALL            = "all"
 | 
			
		||||
	USER_NOTIFY_MENTION        = "mention"
 | 
			
		||||
	USER_NOTIFY_NONE           = "none"
 | 
			
		||||
	DEFAULT_LOCALE             = "en"
 | 
			
		||||
	USER_AUTH_SERVICE_EMAIL    = "email"
 | 
			
		||||
	USER_AUTH_SERVICE_USERNAME = "username"
 | 
			
		||||
	MIN_PASSWORD_LENGTH        = 5
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
@@ -45,18 +39,16 @@ type User struct {
 | 
			
		||||
	FirstName          string    `json:"first_name"`
 | 
			
		||||
	LastName           string    `json:"last_name"`
 | 
			
		||||
	Roles              string    `json:"roles"`
 | 
			
		||||
	LastActivityAt     int64     `json:"last_activity_at,omitempty"`
 | 
			
		||||
	LastPingAt         int64     `json:"last_ping_at,omitempty"`
 | 
			
		||||
	AllowMarketing     bool      `json:"allow_marketing,omitempty"`
 | 
			
		||||
	Props              StringMap `json:"props,omitempty"`
 | 
			
		||||
	NotifyProps        StringMap `json:"notify_props,omitempty"`
 | 
			
		||||
	ThemeProps         StringMap `json:"theme_props,omitempty"`
 | 
			
		||||
	LastPasswordUpdate int64     `json:"last_password_update,omitempty"`
 | 
			
		||||
	LastPictureUpdate  int64     `json:"last_picture_update,omitempty"`
 | 
			
		||||
	FailedAttempts     int       `json:"failed_attempts,omitempty"`
 | 
			
		||||
	Locale             string    `json:"locale"`
 | 
			
		||||
	MfaActive          bool      `json:"mfa_active,omitempty"`
 | 
			
		||||
	MfaSecret          string    `json:"mfa_secret,omitempty"`
 | 
			
		||||
	LastActivityAt     int64     `db:"-" json:"last_activity_at,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValid validates the user and returns an error if it isn't configured
 | 
			
		||||
@@ -95,10 +87,6 @@ func (u *User) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(u.Password) > 128 {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.user.is_valid.pwd.app_error", nil, "user_id="+u.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if u.AuthData != nil && len(*u.AuthData) > 128 {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id)
 | 
			
		||||
	}
 | 
			
		||||
@@ -111,10 +99,6 @@ func (u *User) IsValid() *AppError {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(u.ThemeProps) > 2000 {
 | 
			
		||||
		return NewLocAppError("User.IsValid", "model.user.is_valid.theme.app_error", nil, "user_id="+u.Id)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -136,7 +120,6 @@ func (u *User) PreSave() {
 | 
			
		||||
 | 
			
		||||
	u.Username = strings.ToLower(u.Username)
 | 
			
		||||
	u.Email = strings.ToLower(u.Email)
 | 
			
		||||
	u.Locale = strings.ToLower(u.Locale)
 | 
			
		||||
 | 
			
		||||
	u.CreateAt = GetMillis()
 | 
			
		||||
	u.UpdateAt = u.CreateAt
 | 
			
		||||
@@ -166,7 +149,6 @@ func (u *User) PreSave() {
 | 
			
		||||
func (u *User) PreUpdate() {
 | 
			
		||||
	u.Username = strings.ToLower(u.Username)
 | 
			
		||||
	u.Email = strings.ToLower(u.Email)
 | 
			
		||||
	u.Locale = strings.ToLower(u.Locale)
 | 
			
		||||
	u.UpdateAt = GetMillis()
 | 
			
		||||
 | 
			
		||||
	if u.AuthData != nil && *u.AuthData == "" {
 | 
			
		||||
@@ -191,10 +173,10 @@ func (u *User) PreUpdate() {
 | 
			
		||||
func (u *User) SetDefaultNotifications() {
 | 
			
		||||
	u.NotifyProps = make(map[string]string)
 | 
			
		||||
	u.NotifyProps["email"] = "true"
 | 
			
		||||
	u.NotifyProps["push"] = USER_NOTIFY_MENTION
 | 
			
		||||
	u.NotifyProps["desktop"] = USER_NOTIFY_ALL
 | 
			
		||||
	u.NotifyProps["desktop_sound"] = "true"
 | 
			
		||||
	u.NotifyProps["mention_keys"] = u.Username + ",@" + u.Username
 | 
			
		||||
	u.NotifyProps["all"] = "true"
 | 
			
		||||
	u.NotifyProps["channel"] = "true"
 | 
			
		||||
 | 
			
		||||
	if u.FirstName == "" {
 | 
			
		||||
@@ -230,16 +212,8 @@ func (u *User) ToJson() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Generate a valid strong etag so the browser can cache the results
 | 
			
		||||
func (u *User) Etag() string {
 | 
			
		||||
	return Etag(u.Id, u.UpdateAt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) IsOffline() bool {
 | 
			
		||||
	return (GetMillis()-u.LastPingAt) > USER_OFFLINE_TIMEOUT && (GetMillis()-u.LastActivityAt) > USER_OFFLINE_TIMEOUT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) IsAway() bool {
 | 
			
		||||
	return (GetMillis() - u.LastActivityAt) > USER_AWAY_TIMEOUT
 | 
			
		||||
func (u *User) Etag(showFullName, showEmail bool) string {
 | 
			
		||||
	return Etag(u.Id, u.UpdateAt, showFullName, showEmail)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove any private data from the user object
 | 
			
		||||
@@ -262,7 +236,6 @@ func (u *User) Sanitize(options map[string]bool) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) ClearNonProfileFields() {
 | 
			
		||||
	u.UpdateAt = 0
 | 
			
		||||
	u.Password = ""
 | 
			
		||||
	u.AuthData = new(string)
 | 
			
		||||
	*u.AuthData = ""
 | 
			
		||||
@@ -270,16 +243,20 @@ func (u *User) ClearNonProfileFields() {
 | 
			
		||||
	u.MfaActive = false
 | 
			
		||||
	u.MfaSecret = ""
 | 
			
		||||
	u.EmailVerified = false
 | 
			
		||||
	u.LastPingAt = 0
 | 
			
		||||
	u.AllowMarketing = false
 | 
			
		||||
	u.Props = StringMap{}
 | 
			
		||||
	u.NotifyProps = StringMap{}
 | 
			
		||||
	u.ThemeProps = StringMap{}
 | 
			
		||||
	u.LastPasswordUpdate = 0
 | 
			
		||||
	u.LastPictureUpdate = 0
 | 
			
		||||
	u.FailedAttempts = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) SanitizeProfile(options map[string]bool) {
 | 
			
		||||
	u.ClearNonProfileFields()
 | 
			
		||||
 | 
			
		||||
	u.Sanitize(options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) MakeNonNil() {
 | 
			
		||||
	if u.Props == nil {
 | 
			
		||||
		u.Props = make(map[string]string)
 | 
			
		||||
@@ -324,6 +301,24 @@ func (u *User) GetDisplayName() string {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) GetDisplayNameForPreference(nameFormat string) string {
 | 
			
		||||
	displayName := u.Username
 | 
			
		||||
 | 
			
		||||
	if nameFormat == PREFERENCE_VALUE_DISPLAY_NAME_NICKNAME {
 | 
			
		||||
		if u.Nickname != "" {
 | 
			
		||||
			displayName = u.Nickname
 | 
			
		||||
		} else if fullName := u.GetFullName(); fullName != "" {
 | 
			
		||||
			displayName = fullName
 | 
			
		||||
		}
 | 
			
		||||
	} else if nameFormat == PREFERENCE_VALUE_DISPLAY_NAME_FULL {
 | 
			
		||||
		if fullName := u.GetFullName(); fullName != "" {
 | 
			
		||||
			displayName = fullName
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return displayName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsValidUserRoles(userRoles string) bool {
 | 
			
		||||
 | 
			
		||||
	roles := strings.Split(userRoles, " ")
 | 
			
		||||
@@ -349,13 +344,13 @@ func isValidRole(role string) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Make sure you acually want to use this function. In context.go there are functions to check permssions
 | 
			
		||||
// Make sure you acually want to use this function. In context.go there are functions to check permissions
 | 
			
		||||
// This function should not be used to check permissions.
 | 
			
		||||
func (u *User) IsInRole(inRole string) bool {
 | 
			
		||||
	return IsInRole(u.Roles, inRole)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Make sure you acually want to use this function. In context.go there are functions to check permssions
 | 
			
		||||
// Make sure you acually want to use this function. In context.go there are functions to check permissions
 | 
			
		||||
// This function should not be used to check permissions.
 | 
			
		||||
func IsInRole(userRoles string, inRole string) bool {
 | 
			
		||||
	roles := strings.Split(userRoles, " ")
 | 
			
		||||
@@ -384,17 +379,6 @@ func (u *User) IsLDAPUser() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *User) PreExport() {
 | 
			
		||||
	u.Password = ""
 | 
			
		||||
	u.AuthData = new(string)
 | 
			
		||||
	*u.AuthData = ""
 | 
			
		||||
	u.LastActivityAt = 0
 | 
			
		||||
	u.LastPingAt = 0
 | 
			
		||||
	u.LastPasswordUpdate = 0
 | 
			
		||||
	u.LastPictureUpdate = 0
 | 
			
		||||
	u.FailedAttempts = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserFromJson will decode the input and return a User
 | 
			
		||||
func UserFromJson(data io.Reader) *User {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
@@ -453,6 +437,7 @@ var validUsernameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`)
 | 
			
		||||
var restrictedUsernames = []string{
 | 
			
		||||
	"all",
 | 
			
		||||
	"channel",
 | 
			
		||||
	"matterbot",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsValidUsername(s string) bool {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/mattermost/platform/model/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/mattermost/platform/model/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -20,6 +20,13 @@ import (
 | 
			
		||||
	"github.com/pborman/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"
 | 
			
		||||
	UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | 
			
		||||
	NUMBERS           = "0123456789"
 | 
			
		||||
	SYMBOLS           = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StringInterface map[string]interface{}
 | 
			
		||||
type StringMap map[string]string
 | 
			
		||||
type StringArray []string
 | 
			
		||||
@@ -27,12 +34,12 @@ type EncryptStringMap map[string]string
 | 
			
		||||
 | 
			
		||||
type AppError struct {
 | 
			
		||||
	Id            string                 `json:"id"`
 | 
			
		||||
	Message       string                 `json:"message"`        // Message to be display to the end user without debugging information
 | 
			
		||||
	DetailedError string                 `json:"detailed_error"` // Internal error string to help the developer
 | 
			
		||||
	RequestId     string                 `json:"request_id"`     // The RequestId that's also set in the header
 | 
			
		||||
	StatusCode    int                    `json:"status_code"`    // The http status code
 | 
			
		||||
	Where         string                 `json:"-"`              // The function where it happened in the form of Struct.Func
 | 
			
		||||
	IsOAuth       bool                   `json:"is_oauth"`       // Whether the error is OAuth specific
 | 
			
		||||
	Message       string                 `json:"message"`               // Message to be display to the end user without debugging information
 | 
			
		||||
	DetailedError string                 `json:"detailed_error"`        // Internal error string to help the developer
 | 
			
		||||
	RequestId     string                 `json:"request_id,omitempty"`  // The RequestId that's also set in the header
 | 
			
		||||
	StatusCode    int                    `json:"status_code,omitempty"` // The http status code
 | 
			
		||||
	Where         string                 `json:"-"`                     // The function where it happened in the form of Struct.Func
 | 
			
		||||
	IsOAuth       bool                   `json:"is_oauth,omitempty"`    // Whether the error is OAuth specific
 | 
			
		||||
	params        map[string]interface{} `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -315,10 +322,9 @@ func Etag(parts ...interface{}) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var validHashtag = regexp.MustCompile(`^(#[A-Za-zäöüÄÖÜß]+[A-Za-z0-9äöüÄÖÜß_\-]*[A-Za-z0-9äöüÄÖÜß])$`)
 | 
			
		||||
var puncStart = regexp.MustCompile(`^[.,()&$!\?\[\]{}':;\\<>\-+=%^*|]+`)
 | 
			
		||||
var puncStart = regexp.MustCompile(`^[^\pL\d\s#]+`)
 | 
			
		||||
var hashtagStart = regexp.MustCompile(`^#{2,}`)
 | 
			
		||||
var puncEnd = regexp.MustCompile(`[.,()&$#!\?\[\]{}':;\\<>\-+=%^*|]+$`)
 | 
			
		||||
var puncEndWildcard = regexp.MustCompile(`[.,()&$#!\?\[\]{}':;\\<>\-+=%^|]+$`)
 | 
			
		||||
var puncEnd = regexp.MustCompile(`[^\pL\d\s]+$`)
 | 
			
		||||
 | 
			
		||||
func ParseHashtags(text string) (string, string) {
 | 
			
		||||
	words := strings.Fields(text)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/mattermost/platform/model/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/mattermost/platform/model/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,10 @@ import (
 | 
			
		||||
// It should be maitained in chronological order with most current
 | 
			
		||||
// release at the front of the list.
 | 
			
		||||
var versions = []string{
 | 
			
		||||
	"3.4.0",
 | 
			
		||||
	"3.3.0",
 | 
			
		||||
	"3.2.0",
 | 
			
		||||
	"3.1.0",
 | 
			
		||||
	"3.0.0",
 | 
			
		||||
	"2.2.0",
 | 
			
		||||
	"2.1.0",
 | 
			
		||||
@@ -33,6 +37,7 @@ var CurrentVersion string = versions[0]
 | 
			
		||||
var BuildNumber string
 | 
			
		||||
var BuildDate string
 | 
			
		||||
var BuildHash string
 | 
			
		||||
var BuildHashEnterprise string
 | 
			
		||||
var BuildEnterpriseReady string
 | 
			
		||||
var versionsWithoutHotFixes []string
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								vendor/github.com/mattermost/platform/model/websocket_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/mattermost/platform/model/websocket_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WebSocketClient struct {
 | 
			
		||||
	Url             string          // The location of the server like "ws://localhost:8065"
 | 
			
		||||
	ApiUrl          string          // The api location of the server like "ws://localhost:8065/api/v3"
 | 
			
		||||
	Conn            *websocket.Conn // The WebSocket connection
 | 
			
		||||
	AuthToken       string          // The token used to open the WebSocket
 | 
			
		||||
	Sequence        int64           // The ever-incrementing sequence attached to each WebSocket action
 | 
			
		||||
	EventChannel    chan *WebSocketEvent
 | 
			
		||||
	ResponseChannel chan *WebSocketResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWebSocketClient constructs a new WebSocket client with convienence
 | 
			
		||||
// methods for talking to the server.
 | 
			
		||||
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
 | 
			
		||||
	header := http.Header{}
 | 
			
		||||
	header.Set(HEADER_AUTH, "BEARER "+authToken)
 | 
			
		||||
	conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", header)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &WebSocketClient{
 | 
			
		||||
		url,
 | 
			
		||||
		url + API_URL_SUFFIX,
 | 
			
		||||
		conn,
 | 
			
		||||
		authToken,
 | 
			
		||||
		1,
 | 
			
		||||
		make(chan *WebSocketEvent, 100),
 | 
			
		||||
		make(chan *WebSocketResponse, 100),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wsc *WebSocketClient) Connect() *AppError {
 | 
			
		||||
	header := http.Header{}
 | 
			
		||||
	header.Set(HEADER_AUTH, "BEARER "+wsc.AuthToken)
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ApiUrl+"/users/websocket", header)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wsc *WebSocketClient) Close() {
 | 
			
		||||
	wsc.Conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wsc *WebSocketClient) Listen() {
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			var rawMsg json.RawMessage
 | 
			
		||||
			var err error
 | 
			
		||||
			if _, rawMsg, err = wsc.Conn.ReadMessage(); err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var event WebSocketEvent
 | 
			
		||||
			if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() {
 | 
			
		||||
				wsc.EventChannel <- &event
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var response WebSocketResponse
 | 
			
		||||
			if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() {
 | 
			
		||||
				wsc.ResponseChannel <- &response
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wsc *WebSocketClient) SendMessage(action string, data map[string]interface{}) {
 | 
			
		||||
	req := &WebSocketRequest{}
 | 
			
		||||
	req.Seq = wsc.Sequence
 | 
			
		||||
	req.Action = action
 | 
			
		||||
	req.Data = data
 | 
			
		||||
 | 
			
		||||
	wsc.Sequence++
 | 
			
		||||
 | 
			
		||||
	wsc.Conn.WriteJSON(req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserTyping will push a user_typing event out to all connected users
 | 
			
		||||
// who are in the specified channel
 | 
			
		||||
func (wsc *WebSocketClient) UserTyping(channelId, parentId string) {
 | 
			
		||||
	data := map[string]interface{}{
 | 
			
		||||
		"channel_id": channelId,
 | 
			
		||||
		"parent_id":  parentId,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wsc.SendMessage("user_typing", data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStatuses will return a map of string statuses using user id as the key
 | 
			
		||||
func (wsc *WebSocketClient) GetStatuses() {
 | 
			
		||||
	wsc.SendMessage("get_statuses", nil)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								vendor/github.com/mattermost/platform/model/websocket_message.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/mattermost/platform/model/websocket_message.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	WEBSOCKET_EVENT_TYPING             = "typing"
 | 
			
		||||
	WEBSOCKET_EVENT_POSTED             = "posted"
 | 
			
		||||
	WEBSOCKET_EVENT_POST_EDITED        = "post_edited"
 | 
			
		||||
	WEBSOCKET_EVENT_POST_DELETED       = "post_deleted"
 | 
			
		||||
	WEBSOCKET_EVENT_CHANNEL_DELETED    = "channel_deleted"
 | 
			
		||||
	WEBSOCKET_EVENT_CHANNEL_VIEWED     = "channel_viewed"
 | 
			
		||||
	WEBSOCKET_EVENT_DIRECT_ADDED       = "direct_added"
 | 
			
		||||
	WEBSOCKET_EVENT_NEW_USER           = "new_user"
 | 
			
		||||
	WEBSOCKET_EVENT_LEAVE_TEAM         = "leave_team"
 | 
			
		||||
	WEBSOCKET_EVENT_USER_ADDED         = "user_added"
 | 
			
		||||
	WEBSOCKET_EVENT_USER_UPDATED       = "user_updated"
 | 
			
		||||
	WEBSOCKET_EVENT_USER_REMOVED       = "user_removed"
 | 
			
		||||
	WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
 | 
			
		||||
	WEBSOCKET_EVENT_EPHEMERAL_MESSAGE  = "ephemeral_message"
 | 
			
		||||
	WEBSOCKET_EVENT_STATUS_CHANGE      = "status_change"
 | 
			
		||||
	WEBSOCKET_EVENT_HELLO              = "hello"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WebSocketMessage interface {
 | 
			
		||||
	ToJson() string
 | 
			
		||||
	IsValid() bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WebSocketEvent struct {
 | 
			
		||||
	TeamId    string                 `json:"team_id"`
 | 
			
		||||
	ChannelId string                 `json:"channel_id"`
 | 
			
		||||
	UserId    string                 `json:"user_id"`
 | 
			
		||||
	Event     string                 `json:"event"`
 | 
			
		||||
	Data      map[string]interface{} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebSocketEvent) Add(key string, value interface{}) {
 | 
			
		||||
	m.Data[key] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewWebSocketEvent(teamId string, channelId string, userId string, event string) *WebSocketEvent {
 | 
			
		||||
	return &WebSocketEvent{TeamId: teamId, ChannelId: channelId, UserId: userId, Event: event, Data: make(map[string]interface{})}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *WebSocketEvent) IsValid() bool {
 | 
			
		||||
	return o.Event != ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *WebSocketEvent) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o WebSocketEvent
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WebSocketResponse struct {
 | 
			
		||||
	Status   string                 `json:"status"`
 | 
			
		||||
	SeqReply int64                  `json:"seq_reply,omitempty"`
 | 
			
		||||
	Data     map[string]interface{} `json:"data,omitempty"`
 | 
			
		||||
	Error    *AppError              `json:"error,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebSocketResponse) Add(key string, value interface{}) {
 | 
			
		||||
	m.Data[key] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewWebSocketResponse(status string, seqReply int64, data map[string]interface{}) *WebSocketResponse {
 | 
			
		||||
	return &WebSocketResponse{Status: status, SeqReply: seqReply, Data: data}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewWebSocketError(seqReply int64, err *AppError) *WebSocketResponse {
 | 
			
		||||
	return &WebSocketResponse{Status: STATUS_FAIL, SeqReply: seqReply, Error: err}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *WebSocketResponse) IsValid() bool {
 | 
			
		||||
	return o.Status != ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *WebSocketResponse) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o WebSocketResponse
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/mattermost/platform/model/websocket_request.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/mattermost/platform/model/websocket_request.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
 | 
			
		||||
// See License.txt for license information.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	goi18n "github.com/nicksnyder/go-i18n/i18n"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WebSocketRequest struct {
 | 
			
		||||
	// Client-provided fields
 | 
			
		||||
	Seq    int64                  `json:"seq"`
 | 
			
		||||
	Action string                 `json:"action"`
 | 
			
		||||
	Data   map[string]interface{} `json:"data"`
 | 
			
		||||
 | 
			
		||||
	// Server-provided fields
 | 
			
		||||
	Session Session              `json:"-"`
 | 
			
		||||
	T       goi18n.TranslateFunc `json:"-"`
 | 
			
		||||
	Locale  string               `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *WebSocketRequest) ToJson() string {
 | 
			
		||||
	b, err := json.Marshal(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	} else {
 | 
			
		||||
		return string(b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WebSocketRequestFromJson(data io.Reader) *WebSocketRequest {
 | 
			
		||||
	decoder := json.NewDecoder(data)
 | 
			
		||||
	var o WebSocketRequest
 | 
			
		||||
	err := decoder.Decode(&o)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return &o
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/mattn/go-xmpp/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/mattn/go-xmpp/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
Copyright (c) 2009 The Go Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										881
									
								
								vendor/github.com/mattn/go-xmpp/xmpp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										881
									
								
								vendor/github.com/mattn/go-xmpp/xmpp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,881 @@
 | 
			
		||||
// Copyright 2011 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// TODO(rsc):
 | 
			
		||||
//	More precise error handling.
 | 
			
		||||
//	Presence functionality.
 | 
			
		||||
// TODO(mattn):
 | 
			
		||||
//  Add proxy authentication.
 | 
			
		||||
 | 
			
		||||
// Package xmpp implements a simple Google Talk client
 | 
			
		||||
// using the XMPP protocol described in RFC 3920 and RFC 3921.
 | 
			
		||||
package xmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	nsStream  = "http://etherx.jabber.org/streams"
 | 
			
		||||
	nsTLS     = "urn:ietf:params:xml:ns:xmpp-tls"
 | 
			
		||||
	nsSASL    = "urn:ietf:params:xml:ns:xmpp-sasl"
 | 
			
		||||
	nsBind    = "urn:ietf:params:xml:ns:xmpp-bind"
 | 
			
		||||
	nsClient  = "jabber:client"
 | 
			
		||||
	nsSession = "urn:ietf:params:xml:ns:xmpp-session"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Default TLS configuration options
 | 
			
		||||
var DefaultConfig tls.Config
 | 
			
		||||
 | 
			
		||||
// Cookie is a unique XMPP session identifier
 | 
			
		||||
type Cookie uint64
 | 
			
		||||
 | 
			
		||||
func getCookie() Cookie {
 | 
			
		||||
	var buf [8]byte
 | 
			
		||||
	if _, err := rand.Reader.Read(buf[:]); err != nil {
 | 
			
		||||
		panic("Failed to read random bytes: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return Cookie(binary.LittleEndian.Uint64(buf[:]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client holds XMPP connection opitons
 | 
			
		||||
type Client struct {
 | 
			
		||||
	conn   net.Conn // connection to server
 | 
			
		||||
	jid    string   // Jabber ID for our connection
 | 
			
		||||
	domain string
 | 
			
		||||
	p      *xml.Decoder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) JID() string {
 | 
			
		||||
	return c.jid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func connect(host, user, passwd string) (net.Conn, error) {
 | 
			
		||||
	addr := host
 | 
			
		||||
 | 
			
		||||
	if strings.TrimSpace(host) == "" {
 | 
			
		||||
		a := strings.SplitN(user, "@", 2)
 | 
			
		||||
		if len(a) == 2 {
 | 
			
		||||
			addr = a[1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	a := strings.SplitN(host, ":", 2)
 | 
			
		||||
	if len(a) == 1 {
 | 
			
		||||
		addr += ":5222"
 | 
			
		||||
	}
 | 
			
		||||
	proxy := os.Getenv("HTTP_PROXY")
 | 
			
		||||
	if proxy == "" {
 | 
			
		||||
		proxy = os.Getenv("http_proxy")
 | 
			
		||||
	}
 | 
			
		||||
	if proxy != "" {
 | 
			
		||||
		url, err := url.Parse(proxy)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			addr = url.Host
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	c, err := net.Dial("tcp", addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if proxy != "" {
 | 
			
		||||
		fmt.Fprintf(c, "CONNECT %s HTTP/1.1\r\n", host)
 | 
			
		||||
		fmt.Fprintf(c, "Host: %s\r\n", host)
 | 
			
		||||
		fmt.Fprintf(c, "\r\n")
 | 
			
		||||
		br := bufio.NewReader(c)
 | 
			
		||||
		req, _ := http.NewRequest("CONNECT", host, nil)
 | 
			
		||||
		resp, err := http.ReadResponse(br, req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if resp.StatusCode != 200 {
 | 
			
		||||
			f := strings.SplitN(resp.Status, " ", 2)
 | 
			
		||||
			return nil, errors.New(f[1])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Options are used to specify additional options for new clients, such as a Resource.
 | 
			
		||||
type Options struct {
 | 
			
		||||
	// Host specifies what host to connect to, as either "hostname" or "hostname:port"
 | 
			
		||||
	// If host is not specified, the  DNS SRV should be used to find the host from the domainpart of the JID.
 | 
			
		||||
	// Default the port to 5222.
 | 
			
		||||
	Host string
 | 
			
		||||
 | 
			
		||||
	// User specifies what user to authenticate to the remote server.
 | 
			
		||||
	User string
 | 
			
		||||
 | 
			
		||||
	// Password supplies the password to use for authentication with the remote server.
 | 
			
		||||
	Password string
 | 
			
		||||
 | 
			
		||||
	// Resource specifies an XMPP client resource, like "bot", instead of accepting one
 | 
			
		||||
	// from the server.  Use "" to let the server generate one for your client.
 | 
			
		||||
	Resource string
 | 
			
		||||
 | 
			
		||||
	// OAuthScope provides go-xmpp the required scope for OAuth2 authentication.
 | 
			
		||||
	OAuthScope string
 | 
			
		||||
 | 
			
		||||
	// OAuthToken provides go-xmpp with the required OAuth2 token used to authenticate
 | 
			
		||||
	OAuthToken string
 | 
			
		||||
 | 
			
		||||
	// OAuthXmlNs provides go-xmpp with the required namespaced used for OAuth2 authentication.  This is
 | 
			
		||||
	// provided to the server as the xmlns:auth attribute of the OAuth2 authentication request.
 | 
			
		||||
	OAuthXmlNs string
 | 
			
		||||
 | 
			
		||||
	// TLS Config
 | 
			
		||||
	TLSConfig *tls.Config
 | 
			
		||||
 | 
			
		||||
	// InsecureAllowUnencryptedAuth permits authentication over a TCP connection that has not been promoted to
 | 
			
		||||
	// TLS by STARTTLS; this could leak authentication information over the network, or permit man in the middle
 | 
			
		||||
	// attacks.
 | 
			
		||||
	InsecureAllowUnencryptedAuth bool
 | 
			
		||||
 | 
			
		||||
	// NoTLS directs go-xmpp to not use TLS initially to contact the server; instead, a plain old unencrypted
 | 
			
		||||
	// TCP connection should be used. (Can be combined with StartTLS to support STARTTLS-based servers.)
 | 
			
		||||
	NoTLS bool
 | 
			
		||||
 | 
			
		||||
	// StartTLS directs go-xmpp to STARTTLS if the server supports it; go-xmpp will automatically STARTTLS
 | 
			
		||||
	// if the server requires it regardless of this option.
 | 
			
		||||
	StartTLS bool
 | 
			
		||||
 | 
			
		||||
	// Debug output
 | 
			
		||||
	Debug bool
 | 
			
		||||
 | 
			
		||||
	// Use server sessions
 | 
			
		||||
	Session bool
 | 
			
		||||
 | 
			
		||||
	// Presence Status
 | 
			
		||||
	Status string
 | 
			
		||||
 | 
			
		||||
	// Status message
 | 
			
		||||
	StatusMessage string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient establishes a new Client connection based on a set of Options.
 | 
			
		||||
func (o Options) NewClient() (*Client, error) {
 | 
			
		||||
	host := o.Host
 | 
			
		||||
	c, err := connect(host, o.User, o.Password)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.LastIndex(o.Host, ":") > 0 {
 | 
			
		||||
		host = host[:strings.LastIndex(o.Host, ":")]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := new(Client)
 | 
			
		||||
	if o.NoTLS {
 | 
			
		||||
		client.conn = c
 | 
			
		||||
	} else {
 | 
			
		||||
		var tlsconn *tls.Conn
 | 
			
		||||
		if o.TLSConfig != nil {
 | 
			
		||||
			tlsconn = tls.Client(c, o.TLSConfig)
 | 
			
		||||
		} else {
 | 
			
		||||
			DefaultConfig.ServerName = host
 | 
			
		||||
			tlsconn = tls.Client(c, &DefaultConfig)
 | 
			
		||||
		}
 | 
			
		||||
		if err = tlsconn.Handshake(); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		insecureSkipVerify := DefaultConfig.InsecureSkipVerify
 | 
			
		||||
		if o.TLSConfig != nil {
 | 
			
		||||
			insecureSkipVerify = o.TLSConfig.InsecureSkipVerify
 | 
			
		||||
		}
 | 
			
		||||
		if !insecureSkipVerify {
 | 
			
		||||
			if err = tlsconn.VerifyHostname(host); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		client.conn = tlsconn
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := client.init(&o); err != nil {
 | 
			
		||||
		client.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient creates a new connection to a host given as "hostname" or "hostname:port".
 | 
			
		||||
// If host is not specified, the  DNS SRV should be used to find the host from the domainpart of the JID.
 | 
			
		||||
// Default the port to 5222.
 | 
			
		||||
func NewClient(host, user, passwd string, debug bool) (*Client, error) {
 | 
			
		||||
	opts := Options{
 | 
			
		||||
		Host:     host,
 | 
			
		||||
		User:     user,
 | 
			
		||||
		Password: passwd,
 | 
			
		||||
		Debug:    debug,
 | 
			
		||||
		Session:  false,
 | 
			
		||||
	}
 | 
			
		||||
	return opts.NewClient()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClientNoTLS creates a new client without TLS
 | 
			
		||||
func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) {
 | 
			
		||||
	opts := Options{
 | 
			
		||||
		Host:     host,
 | 
			
		||||
		User:     user,
 | 
			
		||||
		Password: passwd,
 | 
			
		||||
		NoTLS:    true,
 | 
			
		||||
		Debug:    debug,
 | 
			
		||||
		Session:  false,
 | 
			
		||||
	}
 | 
			
		||||
	return opts.NewClient()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the XMPP connection
 | 
			
		||||
func (c *Client) Close() error {
 | 
			
		||||
	if c.conn != (*tls.Conn)(nil) {
 | 
			
		||||
		return c.conn.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string {
 | 
			
		||||
	h := func(text string) []byte {
 | 
			
		||||
		h := md5.New()
 | 
			
		||||
		h.Write([]byte(text))
 | 
			
		||||
		return h.Sum(nil)
 | 
			
		||||
	}
 | 
			
		||||
	hex := func(bytes []byte) string {
 | 
			
		||||
		return fmt.Sprintf("%x", bytes)
 | 
			
		||||
	}
 | 
			
		||||
	kd := func(secret, data string) []byte {
 | 
			
		||||
		return h(secret + ":" + data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	a1 := string(h(username+":"+realm+":"+passwd)) + ":" + nonce + ":" + cnonceStr
 | 
			
		||||
	a2 := authenticate + ":" + digestURI
 | 
			
		||||
	response := hex(kd(hex(h(a1)), nonce+":"+nonceCountStr+":"+cnonceStr+":auth:"+hex(h(a2))))
 | 
			
		||||
	return response
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cnonce() string {
 | 
			
		||||
	randSize := big.NewInt(0)
 | 
			
		||||
	randSize.Lsh(big.NewInt(1), 64)
 | 
			
		||||
	cn, err := rand.Int(rand.Reader, randSize)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%016x", cn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) init(o *Options) error {
 | 
			
		||||
 | 
			
		||||
	var domain string
 | 
			
		||||
	var user string
 | 
			
		||||
	a := strings.SplitN(o.User, "@", 2)
 | 
			
		||||
	if len(o.User) > 0 {
 | 
			
		||||
		if len(a) != 2 {
 | 
			
		||||
			return errors.New("xmpp: invalid username (want user@domain): " + o.User)
 | 
			
		||||
		}
 | 
			
		||||
		user = a[0]
 | 
			
		||||
		domain = a[1]
 | 
			
		||||
	} // Otherwise, we'll be attempting ANONYMOUS
 | 
			
		||||
 | 
			
		||||
	// Declare intent to be a jabber client and gather stream features.
 | 
			
		||||
	f, err := c.startStream(o, domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the server requires we STARTTLS, attempt to do so.
 | 
			
		||||
	if f, err = c.startTLSIfRequired(f, o, domain); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.User == "" && o.Password == "" {
 | 
			
		||||
		foundAnonymous := false
 | 
			
		||||
		for _, m := range f.Mechanisms.Mechanism {
 | 
			
		||||
			if m == "ANONYMOUS" {
 | 
			
		||||
				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
 | 
			
		||||
				foundAnonymous = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !foundAnonymous {
 | 
			
		||||
			return fmt.Errorf("ANONYMOUS authentication is not an option and username and password were not specified")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// Even digest forms of authentication are unsafe if we do not know that the host
 | 
			
		||||
		// we are talking to is the actual server, and not a man in the middle playing
 | 
			
		||||
		// proxy.
 | 
			
		||||
		if !c.IsEncrypted() && !o.InsecureAllowUnencryptedAuth {
 | 
			
		||||
			return errors.New("refusing to authenticate over unencrypted TCP connection")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mechanism := ""
 | 
			
		||||
		for _, m := range f.Mechanisms.Mechanism {
 | 
			
		||||
			if m == "X-OAUTH2" && o.OAuthToken != "" && o.OAuthScope != "" {
 | 
			
		||||
				mechanism = m
 | 
			
		||||
				// Oauth authentication: send base64-encoded \x00 user \x00 token.
 | 
			
		||||
				raw := "\x00" + user + "\x00" + o.OAuthToken
 | 
			
		||||
				enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
 | 
			
		||||
				base64.StdEncoding.Encode(enc, []byte(raw))
 | 
			
		||||
				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='X-OAUTH2' auth:service='oauth2' "+
 | 
			
		||||
					"xmlns:auth='%s'>%s</auth>\n", nsSASL, o.OAuthXmlNs, enc)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if m == "PLAIN" {
 | 
			
		||||
				mechanism = m
 | 
			
		||||
				// Plain authentication: send base64-encoded \x00 user \x00 password.
 | 
			
		||||
				raw := "\x00" + user + "\x00" + o.Password
 | 
			
		||||
				enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
 | 
			
		||||
				base64.StdEncoding.Encode(enc, []byte(raw))
 | 
			
		||||
				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", nsSASL, enc)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if m == "DIGEST-MD5" {
 | 
			
		||||
				mechanism = m
 | 
			
		||||
				// Digest-MD5 authentication
 | 
			
		||||
				fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL)
 | 
			
		||||
				var ch saslChallenge
 | 
			
		||||
				if err = c.p.DecodeElement(&ch, nil); err != nil {
 | 
			
		||||
					return errors.New("unmarshal <challenge>: " + err.Error())
 | 
			
		||||
				}
 | 
			
		||||
				b, err := base64.StdEncoding.DecodeString(string(ch))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				tokens := map[string]string{}
 | 
			
		||||
				for _, token := range strings.Split(string(b), ",") {
 | 
			
		||||
					kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
 | 
			
		||||
					if len(kv) == 2 {
 | 
			
		||||
						if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
 | 
			
		||||
							kv[1] = kv[1][1 : len(kv[1])-1]
 | 
			
		||||
						}
 | 
			
		||||
						tokens[kv[0]] = kv[1]
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				realm, _ := tokens["realm"]
 | 
			
		||||
				nonce, _ := tokens["nonce"]
 | 
			
		||||
				qop, _ := tokens["qop"]
 | 
			
		||||
				charset, _ := tokens["charset"]
 | 
			
		||||
				cnonceStr := cnonce()
 | 
			
		||||
				digestURI := "xmpp/" + domain
 | 
			
		||||
				nonceCount := fmt.Sprintf("%08x", 1)
 | 
			
		||||
				digest := saslDigestResponse(user, realm, o.Password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
 | 
			
		||||
				message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
 | 
			
		||||
					"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
 | 
			
		||||
 | 
			
		||||
				fmt.Fprintf(c.conn, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
 | 
			
		||||
 | 
			
		||||
				var rspauth saslRspAuth
 | 
			
		||||
				if err = c.p.DecodeElement(&rspauth, nil); err != nil {
 | 
			
		||||
					return errors.New("unmarshal <challenge>: " + err.Error())
 | 
			
		||||
				}
 | 
			
		||||
				b, err = base64.StdEncoding.DecodeString(string(rspauth))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				fmt.Fprintf(c.conn, "<response xmlns='%s'/>\n", nsSASL)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if mechanism == "" {
 | 
			
		||||
			return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Next message should be either success or failure.
 | 
			
		||||
	name, val, err := next(c.p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	switch v := val.(type) {
 | 
			
		||||
	case *saslSuccess:
 | 
			
		||||
	case *saslFailure:
 | 
			
		||||
		// v.Any is type of sub-element in failure,
 | 
			
		||||
		// which gives a description of what failed.
 | 
			
		||||
		return errors.New("auth failure: " + v.Any.Local)
 | 
			
		||||
	default:
 | 
			
		||||
		return errors.New("expected <success> or <failure>, got <" + name.Local + "> in " + name.Space)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now that we're authenticated, we're supposed to start the stream over again.
 | 
			
		||||
	// Declare intent to be a jabber client.
 | 
			
		||||
	if f, err = c.startStream(o, domain); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Generate a unique cookie
 | 
			
		||||
	cookie := getCookie()
 | 
			
		||||
 | 
			
		||||
	// Send IQ message asking to bind to the local user name.
 | 
			
		||||
	if o.Resource == "" {
 | 
			
		||||
		fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'></bind></iq>\n", cookie, nsBind)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource)
 | 
			
		||||
	}
 | 
			
		||||
	var iq clientIQ
 | 
			
		||||
	if err = c.p.DecodeElement(&iq, nil); err != nil {
 | 
			
		||||
		return errors.New("unmarshal <iq>: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	if &iq.Bind == nil {
 | 
			
		||||
		return errors.New("<iq> result missing <bind>")
 | 
			
		||||
	}
 | 
			
		||||
	c.jid = iq.Bind.Jid // our local id
 | 
			
		||||
	c.domain = domain
 | 
			
		||||
 | 
			
		||||
	if o.Session {
 | 
			
		||||
		//if server support session, open it
 | 
			
		||||
		fmt.Fprintf(c.conn, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>", xmlEscape(domain), cookie, nsSession)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We're connected and can now receive and send messages.
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence xml:lang='en'><show>%s</show><status>%s</status></presence>", o.Status, o.StatusMessage)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startTlsIfRequired examines the server's stream features and, if STARTTLS is required or supported, performs the TLS handshake.
 | 
			
		||||
// f will be updated if the handshake completes, as the new stream's features are typically different from the original.
 | 
			
		||||
func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string) (*streamFeatures, error) {
 | 
			
		||||
	// whether we start tls is a matter of opinion: the server's and the user's.
 | 
			
		||||
	switch {
 | 
			
		||||
	case f.StartTLS == nil:
 | 
			
		||||
		// the server does not support STARTTLS
 | 
			
		||||
		return f, nil
 | 
			
		||||
	case f.StartTLS.Required != nil:
 | 
			
		||||
		// the server requires STARTTLS.
 | 
			
		||||
	case !o.StartTLS:
 | 
			
		||||
		// the user wants STARTTLS and the server supports it.
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(c.conn, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n")
 | 
			
		||||
	var k tlsProceed
 | 
			
		||||
	if err = c.p.DecodeElement(&k, nil); err != nil {
 | 
			
		||||
		return f, errors.New("unmarshal <proceed>: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tc := o.TLSConfig
 | 
			
		||||
	if tc == nil {
 | 
			
		||||
		tc = new(tls.Config)
 | 
			
		||||
		*tc = DefaultConfig
 | 
			
		||||
		//TODO(scott): we should consider using the server's address or reverse lookup
 | 
			
		||||
		tc.ServerName = domain
 | 
			
		||||
	}
 | 
			
		||||
	t := tls.Client(c.conn, tc)
 | 
			
		||||
 | 
			
		||||
	if err = t.Handshake(); err != nil {
 | 
			
		||||
		return f, errors.New("starttls handshake: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	c.conn = t
 | 
			
		||||
 | 
			
		||||
	// restart our declaration of XMPP stream intentions.
 | 
			
		||||
	tf, err := c.startStream(o, domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return f, err
 | 
			
		||||
	}
 | 
			
		||||
	return tf, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startStream will start a new XML decoder for the connection, signal the start of a stream to the server and verify that the server has
 | 
			
		||||
// also started the stream; if o.Debug is true, startStream will tee decoded XML data to stderr.  The features advertised by the server
 | 
			
		||||
// will be returned.
 | 
			
		||||
func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) {
 | 
			
		||||
	if o.Debug {
 | 
			
		||||
		c.p = xml.NewDecoder(tee{c.conn, os.Stderr})
 | 
			
		||||
	} else {
 | 
			
		||||
		c.p = xml.NewDecoder(c.conn)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, "<?xml version='1.0'?>\n"+
 | 
			
		||||
		"<stream:stream to='%s' xmlns='%s'\n"+
 | 
			
		||||
		" xmlns:stream='%s' version='1.0'>\n",
 | 
			
		||||
		xmlEscape(domain), nsClient, nsStream)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We expect the server to start a <stream>.
 | 
			
		||||
	se, err := nextStart(c.p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if se.Name.Space != nsStream || se.Name.Local != "stream" {
 | 
			
		||||
		return nil, fmt.Errorf("expected <stream> but got <%v> in %v", se.Name.Local, se.Name.Space)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now we're in the stream and can use Unmarshal.
 | 
			
		||||
	// Next message should be <features> to tell us authentication options.
 | 
			
		||||
	// See section 4.6 in RFC 3920.
 | 
			
		||||
	f := new(streamFeatures)
 | 
			
		||||
	if err = c.p.DecodeElement(f, nil); err != nil {
 | 
			
		||||
		return f, errors.New("unmarshal <features>: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return f, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEncrypted will return true if the client is connected using a TLS transport, either because it used.
 | 
			
		||||
// TLS to connect from the outset, or because it successfully used STARTTLS to promote a TCP connection to TLS.
 | 
			
		||||
func (c *Client) IsEncrypted() bool {
 | 
			
		||||
	_, ok := c.conn.(*tls.Conn)
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Chat is an incoming or outgoing XMPP chat message.
 | 
			
		||||
type Chat struct {
 | 
			
		||||
	Remote string
 | 
			
		||||
	Type   string
 | 
			
		||||
	Text   string
 | 
			
		||||
	Roster Roster
 | 
			
		||||
	Other  []string
 | 
			
		||||
	Stamp  time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Roster []Contact
 | 
			
		||||
 | 
			
		||||
type Contact struct {
 | 
			
		||||
	Remote string
 | 
			
		||||
	Name   string
 | 
			
		||||
	Group  []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Presence is an XMPP presence notification.
 | 
			
		||||
type Presence struct {
 | 
			
		||||
	From   string
 | 
			
		||||
	To     string
 | 
			
		||||
	Type   string
 | 
			
		||||
	Show   string
 | 
			
		||||
	Status string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type IQ struct {
 | 
			
		||||
	ID    string
 | 
			
		||||
	From  string
 | 
			
		||||
	To    string
 | 
			
		||||
	Type  string
 | 
			
		||||
	Query []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recv waits to receive the next XMPP stanza.
 | 
			
		||||
// Return type is either a presence notification or a chat message.
 | 
			
		||||
func (c *Client) Recv() (stanza interface{}, err error) {
 | 
			
		||||
	for {
 | 
			
		||||
		_, val, err := next(c.p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return Chat{}, err
 | 
			
		||||
		}
 | 
			
		||||
		switch v := val.(type) {
 | 
			
		||||
		case *clientMessage:
 | 
			
		||||
			stamp, _ := time.Parse(
 | 
			
		||||
				"2006-01-02T15:04:05Z",
 | 
			
		||||
				v.Delay.Stamp,
 | 
			
		||||
			)
 | 
			
		||||
			chat := Chat{
 | 
			
		||||
				Remote: v.From,
 | 
			
		||||
				Type:   v.Type,
 | 
			
		||||
				Text:   v.Body,
 | 
			
		||||
				Other:  v.Other,
 | 
			
		||||
				Stamp:  stamp,
 | 
			
		||||
			}
 | 
			
		||||
			return chat, nil
 | 
			
		||||
		case *clientQuery:
 | 
			
		||||
			var r Roster
 | 
			
		||||
			for _, item := range v.Item {
 | 
			
		||||
				r = append(r, Contact{item.Jid, item.Name, item.Group})
 | 
			
		||||
			}
 | 
			
		||||
			return Chat{Type: "roster", Roster: r}, nil
 | 
			
		||||
		case *clientPresence:
 | 
			
		||||
			return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
 | 
			
		||||
		case *clientIQ:
 | 
			
		||||
			return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type, Query: v.Query}, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send sends the message wrapped inside an XMPP message stanza body.
 | 
			
		||||
func (c *Client) Send(chat Chat) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<body>%s</body></message>",
 | 
			
		||||
		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendOrg sends the original text without being wrapped in an XMPP message stanza.
 | 
			
		||||
func (c *Client) SendOrg(org string) (n int, err error) {
 | 
			
		||||
	return fmt.Fprint(c.conn, org)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) SendPresence(presence Presence) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(c.conn, "<presence from='%s' to='%s'/>", xmlEscape(presence.From), xmlEscape(presence.To))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendHtml sends the message as HTML as defined by XEP-0071
 | 
			
		||||
func (c *Client) SendHtml(chat Chat) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+
 | 
			
		||||
		"<body>%s</body>"+
 | 
			
		||||
		"<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html></message>",
 | 
			
		||||
		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Roster asks for the chat roster.
 | 
			
		||||
func (c *Client) Roster() error {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<iq from='%s' type='get' id='roster1'><query xmlns='jabber:iq:roster'/></iq>\n", xmlEscape(c.jid))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 3920  C.1  Streams name space
 | 
			
		||||
type streamFeatures struct {
 | 
			
		||||
	XMLName    xml.Name `xml:"http://etherx.jabber.org/streams features"`
 | 
			
		||||
	StartTLS   *tlsStartTLS
 | 
			
		||||
	Mechanisms saslMechanisms
 | 
			
		||||
	Bind       bindBind
 | 
			
		||||
	Session    bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type streamError struct {
 | 
			
		||||
	XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
 | 
			
		||||
	Any     xml.Name
 | 
			
		||||
	Text    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 3920  C.3  TLS name space
 | 
			
		||||
type tlsStartTLS struct {
 | 
			
		||||
	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
 | 
			
		||||
	Required *string  `xml:"required"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tlsProceed struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tlsFailure struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls failure"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 3920  C.4  SASL name space
 | 
			
		||||
type saslMechanisms struct {
 | 
			
		||||
	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
 | 
			
		||||
	Mechanism []string `xml:"mechanism"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type saslAuth struct {
 | 
			
		||||
	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
 | 
			
		||||
	Mechanism string   `xml:",attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type saslChallenge string
 | 
			
		||||
 | 
			
		||||
type saslRspAuth string
 | 
			
		||||
 | 
			
		||||
type saslResponse string
 | 
			
		||||
 | 
			
		||||
type saslAbort struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl abort"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type saslSuccess struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl success"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type saslFailure struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
 | 
			
		||||
	Any     xml.Name `xml:",any"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 3920  C.5  Resource binding name space
 | 
			
		||||
type bindBind struct {
 | 
			
		||||
	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
 | 
			
		||||
	Resource string
 | 
			
		||||
	Jid      string `xml:"jid"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 3921  B.1  jabber:client
 | 
			
		||||
type clientMessage struct {
 | 
			
		||||
	XMLName xml.Name `xml:"jabber:client message"`
 | 
			
		||||
	From    string   `xml:"from,attr"`
 | 
			
		||||
	ID      string   `xml:"id,attr"`
 | 
			
		||||
	To      string   `xml:"to,attr"`
 | 
			
		||||
	Type    string   `xml:"type,attr"` // chat, error, groupchat, headline, or normal
 | 
			
		||||
 | 
			
		||||
	// These should technically be []clientText, but string is much more convenient.
 | 
			
		||||
	Subject string `xml:"subject"`
 | 
			
		||||
	Body    string `xml:"body"`
 | 
			
		||||
	Thread  string `xml:"thread"`
 | 
			
		||||
 | 
			
		||||
	// Any hasn't matched element
 | 
			
		||||
	Other []string `xml:",any"`
 | 
			
		||||
 | 
			
		||||
	Delay Delay `xml:"delay"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Delay struct {
 | 
			
		||||
	Stamp string `xml:"stamp,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientText struct {
 | 
			
		||||
	Lang string `xml:",attr"`
 | 
			
		||||
	Body string `xml:"chardata"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientPresence struct {
 | 
			
		||||
	XMLName xml.Name `xml:"jabber:client presence"`
 | 
			
		||||
	From    string   `xml:"from,attr"`
 | 
			
		||||
	ID      string   `xml:"id,attr"`
 | 
			
		||||
	To      string   `xml:"to,attr"`
 | 
			
		||||
	Type    string   `xml:"type,attr"` // error, probe, subscribe, subscribed, unavailable, unsubscribe, unsubscribed
 | 
			
		||||
	Lang    string   `xml:"lang,attr"`
 | 
			
		||||
 | 
			
		||||
	Show     string `xml:"show"`   // away, chat, dnd, xa
 | 
			
		||||
	Status   string `xml:"status"` // sb []clientText
 | 
			
		||||
	Priority string `xml:"priority,attr"`
 | 
			
		||||
	Error    *clientError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientIQ struct { // info/query
 | 
			
		||||
	XMLName xml.Name `xml:"jabber:client iq"`
 | 
			
		||||
	From    string   `xml:"from,attr"`
 | 
			
		||||
	ID      string   `xml:"id,attr"`
 | 
			
		||||
	To      string   `xml:"to,attr"`
 | 
			
		||||
	Type    string   `xml:"type,attr"` // error, get, result, set
 | 
			
		||||
	Query   []byte   `xml:",innerxml"`
 | 
			
		||||
	Error   clientError
 | 
			
		||||
	Bind    bindBind
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientError struct {
 | 
			
		||||
	XMLName xml.Name `xml:"jabber:client error"`
 | 
			
		||||
	Code    string   `xml:",attr"`
 | 
			
		||||
	Type    string   `xml:",attr"`
 | 
			
		||||
	Any     xml.Name
 | 
			
		||||
	Text    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientQuery struct {
 | 
			
		||||
	Item []rosterItem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rosterItem struct {
 | 
			
		||||
	XMLName      xml.Name `xml:"jabber:iq:roster item"`
 | 
			
		||||
	Jid          string   `xml:",attr"`
 | 
			
		||||
	Name         string   `xml:",attr"`
 | 
			
		||||
	Subscription string   `xml:",attr"`
 | 
			
		||||
	Group        []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan XML token stream to find next StartElement.
 | 
			
		||||
func nextStart(p *xml.Decoder) (xml.StartElement, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		t, err := p.Token()
 | 
			
		||||
		if err != nil && err != io.EOF || t == nil {
 | 
			
		||||
			return xml.StartElement{}, err
 | 
			
		||||
		}
 | 
			
		||||
		switch t := t.(type) {
 | 
			
		||||
		case xml.StartElement:
 | 
			
		||||
			return t, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan XML token stream for next element and save into val.
 | 
			
		||||
// If val == nil, allocate new element based on proto map.
 | 
			
		||||
// Either way, return val.
 | 
			
		||||
func next(p *xml.Decoder) (xml.Name, interface{}, error) {
 | 
			
		||||
	// Read start element to find out what type we want.
 | 
			
		||||
	se, err := nextStart(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xml.Name{}, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Put it in an interface and allocate one.
 | 
			
		||||
	var nv interface{}
 | 
			
		||||
	switch se.Name.Space + " " + se.Name.Local {
 | 
			
		||||
	case nsStream + " features":
 | 
			
		||||
		nv = &streamFeatures{}
 | 
			
		||||
	case nsStream + " error":
 | 
			
		||||
		nv = &streamError{}
 | 
			
		||||
	case nsTLS + " starttls":
 | 
			
		||||
		nv = &tlsStartTLS{}
 | 
			
		||||
	case nsTLS + " proceed":
 | 
			
		||||
		nv = &tlsProceed{}
 | 
			
		||||
	case nsTLS + " failure":
 | 
			
		||||
		nv = &tlsFailure{}
 | 
			
		||||
	case nsSASL + " mechanisms":
 | 
			
		||||
		nv = &saslMechanisms{}
 | 
			
		||||
	case nsSASL + " challenge":
 | 
			
		||||
		nv = ""
 | 
			
		||||
	case nsSASL + " response":
 | 
			
		||||
		nv = ""
 | 
			
		||||
	case nsSASL + " abort":
 | 
			
		||||
		nv = &saslAbort{}
 | 
			
		||||
	case nsSASL + " success":
 | 
			
		||||
		nv = &saslSuccess{}
 | 
			
		||||
	case nsSASL + " failure":
 | 
			
		||||
		nv = &saslFailure{}
 | 
			
		||||
	case nsBind + " bind":
 | 
			
		||||
		nv = &bindBind{}
 | 
			
		||||
	case nsClient + " message":
 | 
			
		||||
		nv = &clientMessage{}
 | 
			
		||||
	case nsClient + " presence":
 | 
			
		||||
		nv = &clientPresence{}
 | 
			
		||||
	case nsClient + " iq":
 | 
			
		||||
		nv = &clientIQ{}
 | 
			
		||||
	case nsClient + " error":
 | 
			
		||||
		nv = &clientError{}
 | 
			
		||||
	default:
 | 
			
		||||
		return xml.Name{}, nil, errors.New("unexpected XMPP message " +
 | 
			
		||||
			se.Name.Space + " <" + se.Name.Local + "/>")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Unmarshal into that storage.
 | 
			
		||||
	if err = p.DecodeElement(nv, &se); err != nil {
 | 
			
		||||
		return xml.Name{}, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return se.Name, nv, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var xmlSpecial = map[byte]string{
 | 
			
		||||
	'<':  "<",
 | 
			
		||||
	'>':  ">",
 | 
			
		||||
	'"':  """,
 | 
			
		||||
	'\'': "'",
 | 
			
		||||
	'&':  "&",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func xmlEscape(s string) string {
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	for i := 0; i < len(s); i++ {
 | 
			
		||||
		c := s[i]
 | 
			
		||||
		if s, ok := xmlSpecial[c]; ok {
 | 
			
		||||
			b.WriteString(s)
 | 
			
		||||
		} else {
 | 
			
		||||
			b.WriteByte(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return b.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tee struct {
 | 
			
		||||
	r io.Reader
 | 
			
		||||
	w io.Writer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t tee) Read(p []byte) (n int, err error) {
 | 
			
		||||
	n, err = t.r.Read(p)
 | 
			
		||||
	if n > 0 {
 | 
			
		||||
		t.w.Write(p[0:n])
 | 
			
		||||
		t.w.Write([]byte("\n"))
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_information_query.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_information_query.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
package xmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const IQTypeGet = "get"
 | 
			
		||||
const IQTypeSet = "set"
 | 
			
		||||
const IQTypeResult = "result"
 | 
			
		||||
 | 
			
		||||
func (c *Client) Discovery() (string, error) {
 | 
			
		||||
	const namespace = "http://jabber.org/protocol/disco#items"
 | 
			
		||||
	// use getCookie for a pseudo random id.
 | 
			
		||||
	reqID := strconv.FormatUint(uint64(getCookie()), 10)
 | 
			
		||||
	return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RawInformationQuery sends an information query request to the server.
 | 
			
		||||
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
 | 
			
		||||
	const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
 | 
			
		||||
	return id, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										134
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_muc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_muc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
// Copyright 2013 Flo Lauber <dev@qatfy.at>.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// TODO(flo):
 | 
			
		||||
//   - support password protected MUC rooms
 | 
			
		||||
//   - cleanup signatures of join/leave functions
 | 
			
		||||
package xmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	nsMUC     = "http://jabber.org/protocol/muc"
 | 
			
		||||
	nsMUCUser = "http://jabber.org/protocol/muc#user"
 | 
			
		||||
	NoHistory = 0
 | 
			
		||||
	CharHistory = 1
 | 
			
		||||
	StanzaHistory = 2
 | 
			
		||||
	SecondsHistory = 3
 | 
			
		||||
	SinceHistory = 4
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Send sends room topic wrapped inside an XMPP message stanza body.
 | 
			
		||||
func (c *Client) SendTopic(chat Chat) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
 | 
			
		||||
		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
 | 
			
		||||
	if nick == "" {
 | 
			
		||||
		nick = c.jid
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
 | 
			
		||||
		"<x xmlns='%s'>"+
 | 
			
		||||
		"<history maxchars='0'/></x>\n"+
 | 
			
		||||
		"</presence>",
 | 
			
		||||
		xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// xep-0045 7.2
 | 
			
		||||
func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_date *time.Time) (n int, err error) {
 | 
			
		||||
	if nick == "" {
 | 
			
		||||
		nick = c.jid
 | 
			
		||||
	}
 | 
			
		||||
	switch history_type {
 | 
			
		||||
	case NoHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s' />\n" +
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
			
		||||
	case CharHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<history maxchars='%d'/></x>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
			
		||||
	case StanzaHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<history maxstanzas='%d'/></x>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
			
		||||
	case SecondsHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<history seconds='%d'/></x>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
			
		||||
	case SinceHistory:
 | 
			
		||||
		if history_date != nil {
 | 
			
		||||
			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
				"<x xmlns='%s'>\n" +
 | 
			
		||||
				"<history since='%s'/></x>\n" +
 | 
			
		||||
				"</presence>",
 | 
			
		||||
					xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0, errors.New("Unknown history option")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// xep-0045 7.2.6
 | 
			
		||||
func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_type, history int, history_date *time.Time) (n int, err error) {
 | 
			
		||||
	if nick == "" {
 | 
			
		||||
		nick = c.jid
 | 
			
		||||
	}
 | 
			
		||||
	switch history_type {
 | 
			
		||||
	case NoHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<password>%s</password>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
 | 
			
		||||
	case CharHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<password>%s</password>\n"+
 | 
			
		||||
			"<history maxchars='%d'/></x>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
			
		||||
	case StanzaHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<password>%s</password>\n"+
 | 
			
		||||
			"<history maxstanzas='%d'/></x>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
			
		||||
	case SecondsHistory:
 | 
			
		||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<password>%s</password>\n"+
 | 
			
		||||
			"<history seconds='%d'/></x>\n"+
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
			
		||||
	case SinceHistory:
 | 
			
		||||
		if history_date != nil {
 | 
			
		||||
			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
				"<x xmlns='%s'>\n" +
 | 
			
		||||
				"<password>%s</password>\n"+
 | 
			
		||||
				"<history since='%s'/></x>\n" +
 | 
			
		||||
				"</presence>",
 | 
			
		||||
					xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0, errors.New("Unknown history option")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// xep-0045 7.14
 | 
			
		||||
func (c *Client) LeaveMUC(jid string) (n int, err error) {
 | 
			
		||||
	return fmt.Fprintf(c.conn, "<presence from='%s' to='%s' type='unavailable' />",
 | 
			
		||||
		c.jid, xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_ping.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_ping.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package xmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *Client) PingC2S(jid, server string) error {
 | 
			
		||||
	if jid == "" {
 | 
			
		||||
		jid = c.jid
 | 
			
		||||
	}
 | 
			
		||||
	if server == "" {
 | 
			
		||||
		server = c.domain
 | 
			
		||||
	}
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
 | 
			
		||||
		"<ping xmlns='urn:xmpp:ping'/>\n"+
 | 
			
		||||
		"</iq>",
 | 
			
		||||
		xmlEscape(jid), xmlEscape(server))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) PingS2S(fromServer, toServer string) error {
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
 | 
			
		||||
		"<ping xmlns='urn:xmpp:ping'/>\n"+
 | 
			
		||||
		"</iq>",
 | 
			
		||||
		xmlEscape(fromServer), xmlEscape(toServer))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_subscription.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/mattn/go-xmpp/xmpp_subscription.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
package xmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *Client) ApproveSubscription(jid string) {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
 | 
			
		||||
		xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) RevokeSubscription(jid string) {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
 | 
			
		||||
		xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) RequestSubscription(jid string) {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
 | 
			
		||||
		xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/mreiferson/go-httpclient/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/mreiferson/go-httpclient/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2012 Matt Reiferson
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										237
									
								
								vendor/github.com/mreiferson/go-httpclient/httpclient.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								vendor/github.com/mreiferson/go-httpclient/httpclient.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
			
		||||
/*
 | 
			
		||||
Provides an HTTP Transport that implements the `RoundTripper` interface and
 | 
			
		||||
can be used as a built in replacement for the standard library's, providing:
 | 
			
		||||
 | 
			
		||||
	* connection timeouts
 | 
			
		||||
	* request timeouts
 | 
			
		||||
 | 
			
		||||
This is a thin wrapper around `http.Transport` that sets dial timeouts and uses
 | 
			
		||||
Go's internal timer scheduler to call the Go 1.1+ `CancelRequest()` API.
 | 
			
		||||
*/
 | 
			
		||||
package httpclient
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// returns the current version of the package
 | 
			
		||||
func Version() string {
 | 
			
		||||
	return "0.4.1"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Transport implements the RoundTripper interface and can be used as a replacement
 | 
			
		||||
// for Go's built in http.Transport implementing end-to-end request timeouts.
 | 
			
		||||
//
 | 
			
		||||
// 	transport := &httpclient.Transport{
 | 
			
		||||
// 	    ConnectTimeout: 1*time.Second,
 | 
			
		||||
// 	    ResponseHeaderTimeout: 5*time.Second,
 | 
			
		||||
// 	    RequestTimeout: 10*time.Second,
 | 
			
		||||
// 	}
 | 
			
		||||
// 	defer transport.Close()
 | 
			
		||||
//
 | 
			
		||||
// 	client := &http.Client{Transport: transport}
 | 
			
		||||
// 	req, _ := http.NewRequest("GET", "http://127.0.0.1/test", nil)
 | 
			
		||||
// 	resp, err := client.Do(req)
 | 
			
		||||
// 	if err != nil {
 | 
			
		||||
// 	    return err
 | 
			
		||||
// 	}
 | 
			
		||||
// 	defer resp.Body.Close()
 | 
			
		||||
//
 | 
			
		||||
type Transport struct {
 | 
			
		||||
	// Proxy specifies a function to return a proxy for a given
 | 
			
		||||
	// *http.Request. If the function returns a non-nil error, the
 | 
			
		||||
	// request is aborted with the provided error.
 | 
			
		||||
	// If Proxy is nil or returns a nil *url.URL, no proxy is used.
 | 
			
		||||
	Proxy func(*http.Request) (*url.URL, error)
 | 
			
		||||
 | 
			
		||||
	// Dial specifies the dial function for creating TCP
 | 
			
		||||
	// connections. This will override the Transport's ConnectTimeout and
 | 
			
		||||
	// ReadWriteTimeout settings.
 | 
			
		||||
	// If Dial is nil, a dialer is generated on demand matching the Transport's
 | 
			
		||||
	// options.
 | 
			
		||||
	Dial func(network, addr string) (net.Conn, error)
 | 
			
		||||
 | 
			
		||||
	// TLSClientConfig specifies the TLS configuration to use with
 | 
			
		||||
	// tls.Client. If nil, the default configuration is used.
 | 
			
		||||
	TLSClientConfig *tls.Config
 | 
			
		||||
 | 
			
		||||
	// DisableKeepAlives, if true, prevents re-use of TCP connections
 | 
			
		||||
	// between different HTTP requests.
 | 
			
		||||
	DisableKeepAlives bool
 | 
			
		||||
 | 
			
		||||
	// DisableCompression, if true, prevents the Transport from
 | 
			
		||||
	// requesting compression with an "Accept-Encoding: gzip"
 | 
			
		||||
	// request header when the Request contains no existing
 | 
			
		||||
	// Accept-Encoding value. If the Transport requests gzip on
 | 
			
		||||
	// its own and gets a gzipped response, it's transparently
 | 
			
		||||
	// decoded in the Response.Body. However, if the user
 | 
			
		||||
	// explicitly requested gzip it is not automatically
 | 
			
		||||
	// uncompressed.
 | 
			
		||||
	DisableCompression bool
 | 
			
		||||
 | 
			
		||||
	// MaxIdleConnsPerHost, if non-zero, controls the maximum idle
 | 
			
		||||
	// (keep-alive) to keep per-host.  If zero,
 | 
			
		||||
	// http.DefaultMaxIdleConnsPerHost is used.
 | 
			
		||||
	MaxIdleConnsPerHost int
 | 
			
		||||
 | 
			
		||||
	// ConnectTimeout, if non-zero, is the maximum amount of time a dial will wait for
 | 
			
		||||
	// a connect to complete.
 | 
			
		||||
	ConnectTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// ResponseHeaderTimeout, if non-zero, specifies the amount of
 | 
			
		||||
	// time to wait for a server's response headers after fully
 | 
			
		||||
	// writing the request (including its body, if any). This
 | 
			
		||||
	// time does not include the time to read the response body.
 | 
			
		||||
	ResponseHeaderTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// RequestTimeout, if non-zero, specifies the amount of time for the entire
 | 
			
		||||
	// request to complete (including all of the above timeouts + entire response body).
 | 
			
		||||
	// This should never be less than the sum total of the above two timeouts.
 | 
			
		||||
	RequestTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// ReadWriteTimeout, if non-zero, will set a deadline for every Read and
 | 
			
		||||
	// Write operation on the request connection.
 | 
			
		||||
	ReadWriteTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// TCPWriteBufferSize, the size of the operating system's write
 | 
			
		||||
	// buffer associated with the connection.
 | 
			
		||||
	TCPWriteBufferSize int
 | 
			
		||||
 | 
			
		||||
	// TCPReadBuffserSize, the size of the operating system's read
 | 
			
		||||
	// buffer associated with the connection.
 | 
			
		||||
	TCPReadBufferSize int
 | 
			
		||||
 | 
			
		||||
	starter   sync.Once
 | 
			
		||||
	transport *http.Transport
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close cleans up the Transport, currently a no-op
 | 
			
		||||
func (t *Transport) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) lazyStart() {
 | 
			
		||||
	if t.Dial == nil {
 | 
			
		||||
		t.Dial = func(netw, addr string) (net.Conn, error) {
 | 
			
		||||
			c, err := net.DialTimeout(netw, addr, t.ConnectTimeout)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if t.TCPReadBufferSize != 0 || t.TCPWriteBufferSize != 0 {
 | 
			
		||||
				if tcpCon, ok := c.(*net.TCPConn); ok {
 | 
			
		||||
					if t.TCPWriteBufferSize != 0 {
 | 
			
		||||
						if err = tcpCon.SetWriteBuffer(t.TCPWriteBufferSize); err != nil {
 | 
			
		||||
							return nil, err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if t.TCPReadBufferSize != 0 {
 | 
			
		||||
						if err = tcpCon.SetReadBuffer(t.TCPReadBufferSize); err != nil {
 | 
			
		||||
							return nil, err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					err = errors.New("Not Tcp Connection")
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if t.ReadWriteTimeout > 0 {
 | 
			
		||||
				timeoutConn := &rwTimeoutConn{
 | 
			
		||||
					TCPConn:   c.(*net.TCPConn),
 | 
			
		||||
					rwTimeout: t.ReadWriteTimeout,
 | 
			
		||||
				}
 | 
			
		||||
				return timeoutConn, nil
 | 
			
		||||
			}
 | 
			
		||||
			return c, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.transport = &http.Transport{
 | 
			
		||||
		Dial:                  t.Dial,
 | 
			
		||||
		Proxy:                 t.Proxy,
 | 
			
		||||
		TLSClientConfig:       t.TLSClientConfig,
 | 
			
		||||
		DisableKeepAlives:     t.DisableKeepAlives,
 | 
			
		||||
		DisableCompression:    t.DisableCompression,
 | 
			
		||||
		MaxIdleConnsPerHost:   t.MaxIdleConnsPerHost,
 | 
			
		||||
		ResponseHeaderTimeout: t.ResponseHeaderTimeout,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) CancelRequest(req *http.Request) {
 | 
			
		||||
	t.starter.Do(t.lazyStart)
 | 
			
		||||
 | 
			
		||||
	t.transport.CancelRequest(req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) CloseIdleConnections() {
 | 
			
		||||
	t.starter.Do(t.lazyStart)
 | 
			
		||||
 | 
			
		||||
	t.transport.CloseIdleConnections()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) RegisterProtocol(scheme string, rt http.RoundTripper) {
 | 
			
		||||
	t.starter.Do(t.lazyStart)
 | 
			
		||||
 | 
			
		||||
	t.transport.RegisterProtocol(scheme, rt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
 | 
			
		||||
	t.starter.Do(t.lazyStart)
 | 
			
		||||
 | 
			
		||||
	if t.RequestTimeout > 0 {
 | 
			
		||||
		timer := time.AfterFunc(t.RequestTimeout, func() {
 | 
			
		||||
			t.transport.CancelRequest(req)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		resp, err = t.transport.RoundTrip(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			timer.Stop()
 | 
			
		||||
		} else {
 | 
			
		||||
			resp.Body = &bodyCloseInterceptor{ReadCloser: resp.Body, timer: timer}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		resp, err = t.transport.RoundTrip(req)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type bodyCloseInterceptor struct {
 | 
			
		||||
	io.ReadCloser
 | 
			
		||||
	timer *time.Timer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bci *bodyCloseInterceptor) Close() error {
 | 
			
		||||
	bci.timer.Stop()
 | 
			
		||||
	return bci.ReadCloser.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A net.Conn that sets a deadline for every Read or Write operation
 | 
			
		||||
type rwTimeoutConn struct {
 | 
			
		||||
	*net.TCPConn
 | 
			
		||||
	rwTimeout time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *rwTimeoutConn) Read(b []byte) (int, error) {
 | 
			
		||||
	err := c.TCPConn.SetDeadline(time.Now().Add(c.rwTimeout))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return c.TCPConn.Read(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *rwTimeoutConn) Write(b []byte) (int, error) {
 | 
			
		||||
	err := c.TCPConn.SetDeadline(time.Now().Add(c.rwTimeout))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return c.TCPConn.Write(b)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/mrexodia/wray/examples/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/mrexodia/wray/examples/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "github.com/pythonandchips/wray"
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	wray.RegisterTransports([]wray.Transport{&wray.HttpTransport{}})
 | 
			
		||||
	client := wray.NewFayeClient("http://localhost:5000/faye")
 | 
			
		||||
 | 
			
		||||
	fmt.Println("subscribing")
 | 
			
		||||
	client.Subscribe("/foo", false, func(message wray.Message) {
 | 
			
		||||
		fmt.Println("-------------------------------------------")
 | 
			
		||||
		fmt.Println(message.Data)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	client.Listen()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/github.com/mrexodia/wray/examples/publish.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/mrexodia/wray/examples/publish.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "github.com/pythonandchips/wray"
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
  wray.RegisterTransports([]wray.Transport{ &gofaye.HttpTransport{} })
 | 
			
		||||
  client := wray.NewFayeClient("http://localhost:5000/faye")
 | 
			
		||||
 | 
			
		||||
  params := map[string]interface{}{"hello": "from golang"}
 | 
			
		||||
  fmt.Println("sending")
 | 
			
		||||
  client.Publish("/foo", params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										140
									
								
								vendor/github.com/mrexodia/wray/go_faye.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								vendor/github.com/mrexodia/wray/go_faye.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
package wray
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	UNCONNECTED  = 1
 | 
			
		||||
	CONNECTING   = 2
 | 
			
		||||
	CONNECTED    = 3
 | 
			
		||||
	DISCONNECTED = 4
 | 
			
		||||
 | 
			
		||||
	HANDSHAKE = "handshake"
 | 
			
		||||
	RETRY     = "retry"
 | 
			
		||||
	NONE      = "none"
 | 
			
		||||
 | 
			
		||||
	CONNECTION_TIMEOUT = 60.0
 | 
			
		||||
	DEFAULT_RETRY      = 5.0
 | 
			
		||||
	MAX_REQUEST_SIZE   = 2048
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	MANDATORY_CONNECTION_TYPES = []string{"long-polling"}
 | 
			
		||||
	registeredTransports       = []Transport{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FayeClient struct {
 | 
			
		||||
	state         int
 | 
			
		||||
	url           string
 | 
			
		||||
	subscriptions []Subscription
 | 
			
		||||
	transport     Transport
 | 
			
		||||
	clientId      string
 | 
			
		||||
	schedular     Schedular
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Subscription struct {
 | 
			
		||||
	channel  string
 | 
			
		||||
	callback func(Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SubscriptionPromise struct {
 | 
			
		||||
	subscription Subscription
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFayeClient(url string) *FayeClient {
 | 
			
		||||
	schedular := ChannelSchedular{}
 | 
			
		||||
	client := &FayeClient{url: url, state: UNCONNECTED, schedular: schedular}
 | 
			
		||||
	return client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) handshake() {
 | 
			
		||||
	t, err := SelectTransport(self, MANDATORY_CONNECTION_TYPES, []string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic("No usable transports available")
 | 
			
		||||
	}
 | 
			
		||||
	self.transport = t
 | 
			
		||||
	self.transport.setUrl(self.url)
 | 
			
		||||
	self.state = CONNECTING
 | 
			
		||||
	handshakeParams := map[string]interface{}{"channel": "/meta/handshake",
 | 
			
		||||
		"version":                  "1.0",
 | 
			
		||||
		"supportedConnectionTypes": []string{"long-polling"}}
 | 
			
		||||
	response, err := self.transport.send(handshakeParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Handshake failed. Retry in 10 seconds")
 | 
			
		||||
		self.state = UNCONNECTED
 | 
			
		||||
		self.schedular.wait(10*time.Second, func() {
 | 
			
		||||
			fmt.Println("retying handshake")
 | 
			
		||||
			self.handshake()
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	self.clientId = response.clientId
 | 
			
		||||
	self.state = CONNECTED
 | 
			
		||||
	self.transport, err = SelectTransport(self, response.supportedConnectionTypes, []string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic("Server does not support any available transports. Supported transports: " + strings.Join(response.supportedConnectionTypes, ","))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) Subscribe(channel string, force bool, callback func(Message)) SubscriptionPromise {
 | 
			
		||||
	if self.state == UNCONNECTED {
 | 
			
		||||
		self.handshake()
 | 
			
		||||
	}
 | 
			
		||||
	subscriptionParams := map[string]interface{}{"channel": "/meta/subscribe", "clientId": self.clientId, "subscription": channel, "id": "1"}
 | 
			
		||||
	subscription := Subscription{channel: channel, callback: callback}
 | 
			
		||||
	//TODO: deal with subscription failures
 | 
			
		||||
	self.transport.send(subscriptionParams)
 | 
			
		||||
	self.subscriptions = append(self.subscriptions, subscription)
 | 
			
		||||
	return SubscriptionPromise{subscription}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) handleResponse(response Response) {
 | 
			
		||||
	for _, message := range response.messages {
 | 
			
		||||
		for _, subscription := range self.subscriptions {
 | 
			
		||||
			matched, _ := filepath.Match(subscription.channel, message.Channel)
 | 
			
		||||
			if matched {
 | 
			
		||||
				go subscription.callback(message)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) connect() {
 | 
			
		||||
	connectParams := map[string]interface{}{"channel": "/meta/connect", "clientId": self.clientId, "connectionType": self.transport.connectionType()}
 | 
			
		||||
	responseChannel := make(chan Response)
 | 
			
		||||
	go func() {
 | 
			
		||||
		response, _ := self.transport.send(connectParams)
 | 
			
		||||
		responseChannel <- response
 | 
			
		||||
	}()
 | 
			
		||||
	self.listen(responseChannel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) listen(responseChannel chan Response) {
 | 
			
		||||
	response := <-responseChannel
 | 
			
		||||
	if response.successful == true {
 | 
			
		||||
		go self.handleResponse(response)
 | 
			
		||||
	} else {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) Listen() {
 | 
			
		||||
	for {
 | 
			
		||||
		self.connect()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *FayeClient) Publish(channel string, data map[string]interface{}) {
 | 
			
		||||
	if self.state != CONNECTED {
 | 
			
		||||
		self.handshake()
 | 
			
		||||
	}
 | 
			
		||||
	publishParams := map[string]interface{}{"channel": channel, "data": data, "clientId": self.clientId}
 | 
			
		||||
	self.transport.send(publishParams)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RegisterTransports(transports []Transport) {
 | 
			
		||||
	registeredTransports = transports
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								vendor/github.com/mrexodia/wray/http_transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/mrexodia/wray/http_transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
package wray
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type HttpTransport struct {
 | 
			
		||||
	url      string
 | 
			
		||||
	SendHook func(data map[string]interface{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self HttpTransport) isUsable(clientUrl string) bool {
 | 
			
		||||
	parsedUrl, err := url.Parse(clientUrl)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self HttpTransport) connectionType() string {
 | 
			
		||||
	return "long-polling"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self HttpTransport) send(data map[string]interface{}) (Response, error) {
 | 
			
		||||
	if self.SendHook != nil {
 | 
			
		||||
		self.SendHook(data)
 | 
			
		||||
	}
 | 
			
		||||
	dataBytes, _ := json.Marshal(data)
 | 
			
		||||
	buffer := bytes.NewBuffer(dataBytes)
 | 
			
		||||
	responseData, err := http.Post(self.url, "application/json", buffer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Response{}, err
 | 
			
		||||
	}
 | 
			
		||||
	if responseData.StatusCode != 200 {
 | 
			
		||||
		return Response{}, errors.New(responseData.Status)
 | 
			
		||||
	}
 | 
			
		||||
	readData, _ := ioutil.ReadAll(responseData.Body)
 | 
			
		||||
	responseData.Body.Close()
 | 
			
		||||
	var jsonData []interface{}
 | 
			
		||||
	json.Unmarshal(readData, &jsonData)
 | 
			
		||||
	response := newResponse(jsonData)
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *HttpTransport) setUrl(url string) {
 | 
			
		||||
	self.url = url
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								vendor/github.com/mrexodia/wray/response.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/mrexodia/wray/response.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
package wray
 | 
			
		||||
 | 
			
		||||
type Response struct {
 | 
			
		||||
  id string
 | 
			
		||||
  channel string
 | 
			
		||||
  successful bool
 | 
			
		||||
  clientId string
 | 
			
		||||
  supportedConnectionTypes []string
 | 
			
		||||
  messages []Message
 | 
			
		||||
  error error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
  Channel string
 | 
			
		||||
  Id string
 | 
			
		||||
  Data map[string]interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newResponse(data []interface{}) Response {
 | 
			
		||||
  headerData := data[0].(map[string]interface{})
 | 
			
		||||
  messagesData := data[1.:]
 | 
			
		||||
  messages := parseMessages(messagesData)
 | 
			
		||||
  var id string
 | 
			
		||||
  if headerData["id"] != nil {
 | 
			
		||||
    id = headerData["id"].(string)
 | 
			
		||||
  }
 | 
			
		||||
  supportedConnectionTypes := []string{}
 | 
			
		||||
  if headerData["supportedConnectionTypes"] != nil {
 | 
			
		||||
    d := headerData["supportedConnectionTypes"].([]interface{})
 | 
			
		||||
    for _, sct := range(d) {
 | 
			
		||||
      supportedConnectionTypes = append(supportedConnectionTypes, sct.(string))
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  var clientId string
 | 
			
		||||
  if headerData["clientId"] != nil {
 | 
			
		||||
    clientId = headerData["clientId"].(string)
 | 
			
		||||
  }
 | 
			
		||||
  return Response{id: id,
 | 
			
		||||
                  clientId: clientId,
 | 
			
		||||
                  channel: headerData["channel"].(string),
 | 
			
		||||
                  successful: headerData["successful"].(bool),
 | 
			
		||||
                  messages: messages,
 | 
			
		||||
                  supportedConnectionTypes: supportedConnectionTypes}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseMessages(data []interface{}) []Message {
 | 
			
		||||
  messages := []Message{}
 | 
			
		||||
  for _, messageData := range(data) {
 | 
			
		||||
    m := messageData.(map[string]interface{})
 | 
			
		||||
    var id string
 | 
			
		||||
    if m["id"] != nil {
 | 
			
		||||
      id = m["id"].(string)
 | 
			
		||||
    }
 | 
			
		||||
    message := Message{Channel: m["channel"].(string),
 | 
			
		||||
                       Id: id,
 | 
			
		||||
                       Data: m["data"].(map[string]interface{})}
 | 
			
		||||
    messages = append(messages, message)
 | 
			
		||||
  }
 | 
			
		||||
  return messages
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/mrexodia/wray/schedular.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/mrexodia/wray/schedular.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package wray
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
type Schedular interface {
 | 
			
		||||
	wait(time.Duration, func())
 | 
			
		||||
	delay() time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChannelSchedular struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self ChannelSchedular) wait(delay time.Duration, callback func()) {
 | 
			
		||||
	go func() {
 | 
			
		||||
		time.Sleep(delay)
 | 
			
		||||
		callback()
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self ChannelSchedular) delay() time.Duration {
 | 
			
		||||
	return (1 * time.Minute)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/mrexodia/wray/transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/mrexodia/wray/transport.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package wray
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Transport interface {
 | 
			
		||||
	isUsable(string) bool
 | 
			
		||||
	connectionType() string
 | 
			
		||||
	send(map[string]interface{}) (Response, error)
 | 
			
		||||
	setUrl(string)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SelectTransport(client *FayeClient, transportTypes []string, disabled []string) (Transport, error) {
 | 
			
		||||
	for _, transport := range registeredTransports {
 | 
			
		||||
		if contains(transport.connectionType(), transportTypes) && transport.isUsable(client.url) {
 | 
			
		||||
			return transport, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errors.New("No usable transports available")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/mrexodia/wray/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/mrexodia/wray/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
package wray
 | 
			
		||||
 | 
			
		||||
func contains(target string, slice []string) bool {
 | 
			
		||||
  for _, t := range(slice) {
 | 
			
		||||
    if t == target {
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/nlopes/slack/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/nlopes/slack/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
Copyright (c) 2015, Norberto Lopes
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without modification,
 | 
			
		||||
are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
1. Redistributions of source code must retain the above copyright notice, this
 | 
			
		||||
list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
this list of conditions and the following disclaimer in the documentation and/or
 | 
			
		||||
other materials provided with the distribution.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 | 
			
		||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 | 
			
		||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 | 
			
		||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										190
									
								
								vendor/github.com/nlopes/slack/admin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								vendor/github.com/nlopes/slack/admin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type adminResponse struct {
 | 
			
		||||
	OK    bool   `json:"ok"`
 | 
			
		||||
	Error string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func adminRequest(method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
 | 
			
		||||
	adminResponse := &adminResponse{}
 | 
			
		||||
	err := parseAdminResponse(method, teamName, values, adminResponse, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !adminResponse.OK {
 | 
			
		||||
		return nil, errors.New(adminResponse.Error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return adminResponse, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisableUser disabled a user account, given a user ID
 | 
			
		||||
func (api *Client) DisableUser(teamName string, uid string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {uid},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setInactive", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to disable user with id '%s': %s", uid, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteGuest invites a user to Slack as a single-channel guest
 | 
			
		||||
func (api *Client) InviteGuest(
 | 
			
		||||
	teamName string,
 | 
			
		||||
	channel string,
 | 
			
		||||
	firstName string,
 | 
			
		||||
	lastName string,
 | 
			
		||||
	emailAddress string,
 | 
			
		||||
) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"email":            {emailAddress},
 | 
			
		||||
		"channels":         {channel},
 | 
			
		||||
		"first_name":       {firstName},
 | 
			
		||||
		"last_name":        {lastName},
 | 
			
		||||
		"ultra_restricted": {"1"},
 | 
			
		||||
		"token":            {api.config.token},
 | 
			
		||||
		"set_active":       {"true"},
 | 
			
		||||
		"_attempts":        {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("invite", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to invite single-channel guest: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteRestricted invites a user to Slack as a restricted account
 | 
			
		||||
func (api *Client) InviteRestricted(
 | 
			
		||||
	teamName string,
 | 
			
		||||
	channel string,
 | 
			
		||||
	firstName string,
 | 
			
		||||
	lastName string,
 | 
			
		||||
	emailAddress string,
 | 
			
		||||
) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"email":      {emailAddress},
 | 
			
		||||
		"channels":   {channel},
 | 
			
		||||
		"first_name": {firstName},
 | 
			
		||||
		"last_name":  {lastName},
 | 
			
		||||
		"restricted": {"1"},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("invite", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to restricted account: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteToTeam invites a user to a Slack team
 | 
			
		||||
func (api *Client) InviteToTeam(
 | 
			
		||||
	teamName string,
 | 
			
		||||
	firstName string,
 | 
			
		||||
	lastName string,
 | 
			
		||||
	emailAddress string,
 | 
			
		||||
) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"email":      {emailAddress},
 | 
			
		||||
		"first_name": {firstName},
 | 
			
		||||
		"last_name":  {lastName},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("invite", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to invite to team: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRegular enables the specified user
 | 
			
		||||
func (api *Client) SetRegular(teamName string, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {user},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setRegular", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendSSOBindingEmail sends an SSO binding email to the specified user
 | 
			
		||||
func (api *Client) SendSSOBindingEmail(teamName string, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {user},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("sendSSOBind", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUltraRestricted converts a user into a single-channel guest
 | 
			
		||||
func (api *Client) SetUltraRestricted(teamName, uid, channel string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {uid},
 | 
			
		||||
		"channel":    {channel},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setUltraRestricted", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to ultra-restrict account: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRestricted converts a user into a restricted account
 | 
			
		||||
func (api *Client) SetRestricted(teamName, uid string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {uid},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setRestricted", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to restrict account: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								vendor/github.com/nlopes/slack/attachments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/nlopes/slack/attachments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// AttachmentField contains information for an attachment field
 | 
			
		||||
// An Attachment can contain multiple of these
 | 
			
		||||
type AttachmentField struct {
 | 
			
		||||
	Title string `json:"title"`
 | 
			
		||||
	Value string `json:"value"`
 | 
			
		||||
	Short bool   `json:"short"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttachmentAction is a button to be included in the attachment. Required when
 | 
			
		||||
// using message buttons and otherwise not useful. A maximum of 5 actions may be
 | 
			
		||||
// provided per attachment.
 | 
			
		||||
type AttachmentAction struct {
 | 
			
		||||
	Name    string              `json:"name"`              // Required.
 | 
			
		||||
	Text    string              `json:"text"`              // Required.
 | 
			
		||||
	Style   string              `json:"style,omitempty"`   // Optional. Allowed values: "default", "primary", "danger"
 | 
			
		||||
	Type    string              `json:"type"`              // Required. Must be set to "button"
 | 
			
		||||
	Value   string              `json:"value,omitempty"`   // Optional.
 | 
			
		||||
	Confirm []ConfirmationField `json:"confirm,omitempty"` // Optional.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
 | 
			
		||||
type AttachmentActionCallback struct {
 | 
			
		||||
	Actions    []AttachmentAction `json:"actions"`
 | 
			
		||||
	CallbackID string             `json:"callback_id"`
 | 
			
		||||
	Team       Team               `json:"team"`
 | 
			
		||||
	Channel    Channel            `json:"channel"`
 | 
			
		||||
	User       User               `json:"user"`
 | 
			
		||||
 | 
			
		||||
	OriginalMessage Message `json:"original_message"`
 | 
			
		||||
 | 
			
		||||
	ActionTs     string `json:"action_ts"`
 | 
			
		||||
	MessageTs    string `json:"message_ts"`
 | 
			
		||||
	AttachmentID string `json:"attachment_id"`
 | 
			
		||||
	Token        string `json:"token"`
 | 
			
		||||
	ResponseURL  string `json:"response_url"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConfirmationField are used to ask users to confirm actions
 | 
			
		||||
type ConfirmationField struct {
 | 
			
		||||
	Title       string `json:"title,omitempty"`        // Optional.
 | 
			
		||||
	Text        string `json:"text"`                   // Required.
 | 
			
		||||
	OkText      string `json:"ok_text,omitempty"`      // Optional. Defaults to "Okay"
 | 
			
		||||
	DismissText string `json:"dismiss_text,omitempty"` // Optional. Defaults to "Cancel"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attachment contains all the information for an attachment
 | 
			
		||||
type Attachment struct {
 | 
			
		||||
	Color    string `json:"color,omitempty"`
 | 
			
		||||
	Fallback string `json:"fallback"`
 | 
			
		||||
 | 
			
		||||
	CallbackID string `json:"callback_id,omitempty"`
 | 
			
		||||
 | 
			
		||||
	AuthorName    string `json:"author_name,omitempty"`
 | 
			
		||||
	AuthorSubname string `json:"author_subname,omitempty"`
 | 
			
		||||
	AuthorLink    string `json:"author_link,omitempty"`
 | 
			
		||||
	AuthorIcon    string `json:"author_icon,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Title     string `json:"title,omitempty"`
 | 
			
		||||
	TitleLink string `json:"title_link,omitempty"`
 | 
			
		||||
	Pretext   string `json:"pretext,omitempty"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
 | 
			
		||||
	ImageURL string `json:"image_url,omitempty"`
 | 
			
		||||
	ThumbURL string `json:"thumb_url,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Fields     []AttachmentField  `json:"fields,omitempty"`
 | 
			
		||||
	Actions    []AttachmentAction `json:"actions,omitempty"`
 | 
			
		||||
	MarkdownIn []string           `json:"mrkdwn_in,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Footer     string `json:"footer,omitempty"`
 | 
			
		||||
	FooterIcon string `json:"footer_icon,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Ts int64 `json:"ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								vendor/github.com/nlopes/slack/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/nlopes/slack/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This one was ripped from https://github.com/jpillora/backoff/blob/master/backoff.go
 | 
			
		||||
 | 
			
		||||
// Backoff is a time.Duration counter. It starts at Min.  After every
 | 
			
		||||
// call to Duration() it is multiplied by Factor.  It is capped at
 | 
			
		||||
// Max. It returns to Min on every call to Reset().  Used in
 | 
			
		||||
// conjunction with the time package.
 | 
			
		||||
type backoff struct {
 | 
			
		||||
	attempts int
 | 
			
		||||
	//Factor is the multiplying factor for each increment step
 | 
			
		||||
	Factor float64
 | 
			
		||||
	//Jitter eases contention by randomizing backoff steps
 | 
			
		||||
	Jitter bool
 | 
			
		||||
	//Min and Max are the minimum and maximum values of the counter
 | 
			
		||||
	Min, Max time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the current value of the counter and then multiplies it
 | 
			
		||||
// Factor
 | 
			
		||||
func (b *backoff) Duration() time.Duration {
 | 
			
		||||
	//Zero-values are nonsensical, so we use
 | 
			
		||||
	//them to apply defaults
 | 
			
		||||
	if b.Min == 0 {
 | 
			
		||||
		b.Min = 100 * time.Millisecond
 | 
			
		||||
	}
 | 
			
		||||
	if b.Max == 0 {
 | 
			
		||||
		b.Max = 10 * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	if b.Factor == 0 {
 | 
			
		||||
		b.Factor = 2
 | 
			
		||||
	}
 | 
			
		||||
	//calculate this duration
 | 
			
		||||
	dur := float64(b.Min) * math.Pow(b.Factor, float64(b.attempts))
 | 
			
		||||
	if b.Jitter == true {
 | 
			
		||||
		dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min)
 | 
			
		||||
	}
 | 
			
		||||
	//cap!
 | 
			
		||||
	if dur > float64(b.Max) {
 | 
			
		||||
		return b.Max
 | 
			
		||||
	}
 | 
			
		||||
	//bump attempts count
 | 
			
		||||
	b.attempts++
 | 
			
		||||
	//return as a time.Duration
 | 
			
		||||
	return time.Duration(dur)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//Resets the current value of the counter back to Min
 | 
			
		||||
func (b *backoff) Reset() {
 | 
			
		||||
	b.attempts = 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										261
									
								
								vendor/github.com/nlopes/slack/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								vendor/github.com/nlopes/slack/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,261 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type channelResponseFull struct {
 | 
			
		||||
	Channel      Channel   `json:"channel"`
 | 
			
		||||
	Channels     []Channel `json:"channels"`
 | 
			
		||||
	Purpose      string    `json:"purpose"`
 | 
			
		||||
	Topic        string    `json:"topic"`
 | 
			
		||||
	NotInChannel bool      `json:"not_in_channel"`
 | 
			
		||||
	History
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Channel contains information about the channel
 | 
			
		||||
type Channel struct {
 | 
			
		||||
	groupConversation
 | 
			
		||||
	IsChannel bool `json:"is_channel"`
 | 
			
		||||
	IsGeneral bool `json:"is_general"`
 | 
			
		||||
	IsMember  bool `json:"is_member"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func channelRequest(path string, values url.Values, debug bool) (*channelResponseFull, error) {
 | 
			
		||||
	response := &channelResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveChannel archives the given channel
 | 
			
		||||
func (api *Client) ArchiveChannel(channel string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.archive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveChannel unarchives the given channel
 | 
			
		||||
func (api *Client) UnarchiveChannel(channel string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.unarchive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateChannel creates a channel with the given name and returns a *Channel
 | 
			
		||||
func (api *Client) CreateChannel(channel string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.create", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelHistory retrieves the channel history
 | 
			
		||||
func (api *Client) GetChannelHistory(channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Latest != DEFAULT_HISTORY_LATEST {
 | 
			
		||||
		values.Add("latest", params.Latest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Oldest != DEFAULT_HISTORY_OLDEST {
 | 
			
		||||
		values.Add("oldest", params.Oldest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_HISTORY_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
 | 
			
		||||
		if params.Inclusive {
 | 
			
		||||
			values.Add("inclusive", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("inclusive", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if params.Unreads != DEFAULT_HISTORY_UNREADS {
 | 
			
		||||
		if params.Unreads {
 | 
			
		||||
			values.Add("unreads", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("unreads", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.history", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.History, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelInfo retrieves the given channel
 | 
			
		||||
func (api *Client) GetChannelInfo(channel string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToChannel invites a user to a given channel and returns a *Channel
 | 
			
		||||
func (api *Client) InviteUserToChannel(channel, user string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.invite", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinChannel joins the currently authenticated user to a channel
 | 
			
		||||
func (api *Client) JoinChannel(channel string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.join", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveChannel makes the authenticated user leave the given channel
 | 
			
		||||
func (api *Client) LeaveChannel(channel string) (bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if response.NotInChannel {
 | 
			
		||||
		return response.NotInChannel, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromChannel kicks a user from a given channel
 | 
			
		||||
func (api *Client) KickUserFromChannel(channel, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannels retrieves all the channels
 | 
			
		||||
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if excludeArchived {
 | 
			
		||||
		values.Add("exclude_archived", "1")
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.list", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channels, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelReadMark sets the read mark of a given channel to a specific point
 | 
			
		||||
// Clients should try to avoid making this call too often. When needing to mark a read position, a client should set a
 | 
			
		||||
// timer before making the call. In this way, any further updates needed during the timeout will not generate extra calls
 | 
			
		||||
// (just one per channel). This is useful for when reading scroll-back history, or following a busy live channel. A
 | 
			
		||||
// timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout.
 | 
			
		||||
func (api *Client) SetChannelReadMark(channel, ts string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameChannel renames a given channel
 | 
			
		||||
func (api *Client) RenameChannel(channel, name string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"name":    {name},
 | 
			
		||||
	}
 | 
			
		||||
	// XXX: the created entry in this call returns a string instead of a number
 | 
			
		||||
	// so I may have to do some workaround to solve it.
 | 
			
		||||
	response, err := channelRequest("channels.rename", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelPurpose sets the channel purpose and returns the purpose that was
 | 
			
		||||
// successfully set
 | 
			
		||||
func (api *Client) SetChannelPurpose(channel, purpose string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"purpose": {purpose},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.setPurpose", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Purpose, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelTopic sets the channel topic and returns the topic that was successfully set
 | 
			
		||||
func (api *Client) SetChannelTopic(channel, topic string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"topic":   {topic},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.setTopic", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Topic, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_MESSAGE_USERNAME     = ""
 | 
			
		||||
	DEFAULT_MESSAGE_ASUSER       = false
 | 
			
		||||
	DEFAULT_MESSAGE_PARSE        = ""
 | 
			
		||||
	DEFAULT_MESSAGE_LINK_NAMES   = 0
 | 
			
		||||
	DEFAULT_MESSAGE_UNFURL_LINKS = false
 | 
			
		||||
	DEFAULT_MESSAGE_UNFURL_MEDIA = true
 | 
			
		||||
	DEFAULT_MESSAGE_ICON_URL     = ""
 | 
			
		||||
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
 | 
			
		||||
	DEFAULT_MESSAGE_MARKDOWN     = true
 | 
			
		||||
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type chatResponseFull struct {
 | 
			
		||||
	Channel   string `json:"channel"`
 | 
			
		||||
	Timestamp string `json:"ts"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
 | 
			
		||||
type PostMessageParameters struct {
 | 
			
		||||
	Text        string
 | 
			
		||||
	Username    string
 | 
			
		||||
	AsUser      bool
 | 
			
		||||
	Parse       string
 | 
			
		||||
	LinkNames   int
 | 
			
		||||
	Attachments []Attachment
 | 
			
		||||
	UnfurlLinks bool
 | 
			
		||||
	UnfurlMedia bool
 | 
			
		||||
	IconURL     string
 | 
			
		||||
	IconEmoji   string
 | 
			
		||||
	Markdown    bool `json:"mrkdwn,omitempty"`
 | 
			
		||||
	EscapeText  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
 | 
			
		||||
func NewPostMessageParameters() PostMessageParameters {
 | 
			
		||||
	return PostMessageParameters{
 | 
			
		||||
		Username:    DEFAULT_MESSAGE_USERNAME,
 | 
			
		||||
		AsUser:      DEFAULT_MESSAGE_ASUSER,
 | 
			
		||||
		Parse:       DEFAULT_MESSAGE_PARSE,
 | 
			
		||||
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
 | 
			
		||||
		Attachments: nil,
 | 
			
		||||
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
 | 
			
		||||
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
 | 
			
		||||
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
 | 
			
		||||
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
 | 
			
		||||
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
 | 
			
		||||
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
 | 
			
		||||
	response := &chatResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteMessage deletes a message in a channel
 | 
			
		||||
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"ts":      {messageTimestamp},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := chatRequest("chat.delete", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channel, response.Timestamp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func escapeMessage(message string) string {
 | 
			
		||||
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
 | 
			
		||||
	return replacer.Replace(message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostMessage sends a message to a channel.
 | 
			
		||||
// Message is escaped by default according to https://api.slack.com/docs/formatting
 | 
			
		||||
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
 | 
			
		||||
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
 | 
			
		||||
	if params.EscapeText {
 | 
			
		||||
		text = escapeMessage(text)
 | 
			
		||||
	}
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"text":    {text},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Username != DEFAULT_MESSAGE_USERNAME {
 | 
			
		||||
		values.Set("username", string(params.Username))
 | 
			
		||||
	}
 | 
			
		||||
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
 | 
			
		||||
		values.Set("as_user", "true")
 | 
			
		||||
	}
 | 
			
		||||
	if params.Parse != DEFAULT_MESSAGE_PARSE {
 | 
			
		||||
		values.Set("parse", string(params.Parse))
 | 
			
		||||
	}
 | 
			
		||||
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
 | 
			
		||||
		values.Set("link_names", "1")
 | 
			
		||||
	}
 | 
			
		||||
	if params.Attachments != nil {
 | 
			
		||||
		attachments, err := json.Marshal(params.Attachments)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", "", err
 | 
			
		||||
		}
 | 
			
		||||
		values.Set("attachments", string(attachments))
 | 
			
		||||
	}
 | 
			
		||||
	if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
 | 
			
		||||
		values.Set("unfurl_links", "true")
 | 
			
		||||
	}
 | 
			
		||||
	// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
 | 
			
		||||
	// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
 | 
			
		||||
	if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
 | 
			
		||||
		values.Set("unfurl_links", "false")
 | 
			
		||||
	}
 | 
			
		||||
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
 | 
			
		||||
		values.Set("unfurl_media", "false")
 | 
			
		||||
	}
 | 
			
		||||
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
 | 
			
		||||
		values.Set("icon_url", params.IconURL)
 | 
			
		||||
	}
 | 
			
		||||
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
 | 
			
		||||
		values.Set("icon_emoji", params.IconEmoji)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
 | 
			
		||||
		values.Set("mrkdwn", "false")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := chatRequest("chat.postMessage", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channel, response.Timestamp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateMessage updates a message in a channel
 | 
			
		||||
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"text":    {escapeMessage(text)},
 | 
			
		||||
		"ts":      {timestamp},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := chatRequest("chat.update", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channel, response.Timestamp, response.Text, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/nlopes/slack/comment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/nlopes/slack/comment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// Comment contains all the information relative to a comment
 | 
			
		||||
type Comment struct {
 | 
			
		||||
	ID        string   `json:"id,omitempty"`
 | 
			
		||||
	Created   JSONTime `json:"created,omitempty"`
 | 
			
		||||
	Timestamp JSONTime `json:"timestamp,omitempty"`
 | 
			
		||||
	User      string   `json:"user,omitempty"`
 | 
			
		||||
	Comment   string   `json:"comment,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								vendor/github.com/nlopes/slack/conversation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/nlopes/slack/conversation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// Conversation is the foundation for IM and BaseGroupConversation
 | 
			
		||||
type conversation struct {
 | 
			
		||||
	ID                 string   `json:"id"`
 | 
			
		||||
	Created            JSONTime `json:"created"`
 | 
			
		||||
	IsOpen             bool     `json:"is_open"`
 | 
			
		||||
	LastRead           string   `json:"last_read,omitempty"`
 | 
			
		||||
	Latest             *Message `json:"latest,omitempty"`
 | 
			
		||||
	UnreadCount        int      `json:"unread_count,omitempty"`
 | 
			
		||||
	UnreadCountDisplay int      `json:"unread_count_display,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GroupConversation is the foundation for Group and Channel
 | 
			
		||||
type groupConversation struct {
 | 
			
		||||
	conversation
 | 
			
		||||
	Name       string   `json:"name"`
 | 
			
		||||
	Creator    string   `json:"creator"`
 | 
			
		||||
	IsArchived bool     `json:"is_archived"`
 | 
			
		||||
	Members    []string `json:"members"`
 | 
			
		||||
	NumMembers int      `json:"num_members,omitempty"`
 | 
			
		||||
	Topic      Topic    `json:"topic"`
 | 
			
		||||
	Purpose    Purpose  `json:"purpose"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Topic contains information about the topic
 | 
			
		||||
type Topic struct {
 | 
			
		||||
	Value   string   `json:"value"`
 | 
			
		||||
	Creator string   `json:"creator"`
 | 
			
		||||
	LastSet JSONTime `json:"last_set"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Purpose contains information about the purpose
 | 
			
		||||
type Purpose struct {
 | 
			
		||||
	Value   string   `json:"value"`
 | 
			
		||||
	Creator string   `json:"creator"`
 | 
			
		||||
	LastSet JSONTime `json:"last_set"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SnoozeDebug struct {
 | 
			
		||||
	SnoozeEndDate string `json:"snooze_end_date"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SnoozeInfo struct {
 | 
			
		||||
	SnoozeEnabled   bool        `json:"snooze_enabled,omitempty"`
 | 
			
		||||
	SnoozeEndTime   int         `json:"snooze_endtime,omitempty"`
 | 
			
		||||
	SnoozeRemaining int         `json:"snooze_remaining,omitempty"`
 | 
			
		||||
	SnoozeDebug     SnoozeDebug `json:"snooze_debug,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DNDStatus struct {
 | 
			
		||||
	Enabled            bool `json:"dnd_enabled"`
 | 
			
		||||
	NextStartTimestamp int  `json:"next_dnd_start_ts"`
 | 
			
		||||
	NextEndTimestamp   int  `json:"next_dnd_end_ts"`
 | 
			
		||||
	SnoozeInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dndResponseFull struct {
 | 
			
		||||
	DNDStatus
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dndTeamInfoResponse struct {
 | 
			
		||||
	Users map[string]DNDStatus `json:"users"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dndRequest(path string, values url.Values, debug bool) (*dndResponseFull, error) {
 | 
			
		||||
	response := &dndResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EndDND ends the user's scheduled Do Not Disturb session
 | 
			
		||||
func (api *Client) EndDND() error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("dnd.endDnd", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EndSnooze ends the current user's snooze mode
 | 
			
		||||
func (api *Client) EndSnooze() (*DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := dndRequest("dnd.endSnooze", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.DNDStatus, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDNDInfo provides information about a user's current Do Not Disturb settings.
 | 
			
		||||
func (api *Client) GetDNDInfo(user *string) (*DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if user != nil {
 | 
			
		||||
		values.Set("user", *user)
 | 
			
		||||
	}
 | 
			
		||||
	response, err := dndRequest("dnd.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.DNDStatus, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDNDTeamInfo provides information about a user's current Do Not Disturb settings.
 | 
			
		||||
func (api *Client) GetDNDTeamInfo(users []string) (map[string]DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"users": {strings.Join(users, ",")},
 | 
			
		||||
	}
 | 
			
		||||
	response := &dndTeamInfoResponse{}
 | 
			
		||||
	if err := post("dnd.teamInfo", values, response, api.debug); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.Users, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSnooze adjusts the snooze duration for a user's Do Not Disturb
 | 
			
		||||
// settings. If a snooze session is not already active for the user, invoking
 | 
			
		||||
// this method will begin one for the specified duration.
 | 
			
		||||
func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":       {api.config.token},
 | 
			
		||||
		"num_minutes": {strconv.Itoa(minutes)},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := dndRequest("dnd.setSnooze", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.DNDStatus, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/nlopes/slack/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/nlopes/slack/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type emojiResponseFull struct {
 | 
			
		||||
	Emoji map[string]string `json:"emoji"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetEmoji retrieves all the emojis
 | 
			
		||||
func (api *Client) GetEmoji() (map[string]string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	response := &emojiResponseFull{}
 | 
			
		||||
	err := post("emoji.list", values, response, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.Emoji, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	channels, err := api.GetChannels(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, channel := range channels {
 | 
			
		||||
		fmt.Println(channel.ID)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/nlopes/slack/examples/files/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/nlopes/slack/examples/files/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	params := slack.FileUploadParameters{
 | 
			
		||||
		Title: "Batman Example",
 | 
			
		||||
		//Filetype: "txt",
 | 
			
		||||
		File: "example.txt",
 | 
			
		||||
		//Content:  "Nan Nan Nan Nan Nan Nan Nan Nan Batman",
 | 
			
		||||
	}
 | 
			
		||||
	file, err := api.UploadFile(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Name: %s, URL: %s\n", file.Name, file.URL)
 | 
			
		||||
 | 
			
		||||
	err = api.DeleteFile(file.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("File %s deleted successfully.\n", file.Name)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/nlopes/slack/examples/groups/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/nlopes/slack/examples/groups/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	// If you set debugging, it will log all requests to the console
 | 
			
		||||
	// Useful when encountering issues
 | 
			
		||||
	// api.SetDebug(true)
 | 
			
		||||
	groups, err := api.GetGroups(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, group := range groups {
 | 
			
		||||
		fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/nlopes/slack/examples/messages/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/nlopes/slack/examples/messages/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	params := slack.PostMessageParameters{}
 | 
			
		||||
	attachment := slack.Attachment{
 | 
			
		||||
		Pretext: "some pretext",
 | 
			
		||||
		Text:    "some text",
 | 
			
		||||
		// Uncomment the following part to send a field too
 | 
			
		||||
		/*
 | 
			
		||||
			Fields: []slack.AttachmentField{
 | 
			
		||||
				slack.AttachmentField{
 | 
			
		||||
					Title: "a",
 | 
			
		||||
					Value: "no",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
	params.Attachments = []slack.Attachment{attachment}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage("CHANNEL_ID", "Some text", params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/nlopes/slack/examples/pins/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/nlopes/slack/examples/pins/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   WARNING: This example is destructive in the sense that it create a channel called testpinning
 | 
			
		||||
*/
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		postAsUserName  string
 | 
			
		||||
		postAsUserID    string
 | 
			
		||||
		postToChannelID string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Find the user to post as.
 | 
			
		||||
	authTest, err := api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting channels: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channelName := "testpinning"
 | 
			
		||||
 | 
			
		||||
	// Post as the authenticated user.
 | 
			
		||||
	postAsUserName = authTest.User
 | 
			
		||||
	postAsUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Create a temporary channel
 | 
			
		||||
	channel, err := api.CreateChannel(channelName)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// If the channel exists, that means we just need to unarchive it
 | 
			
		||||
		if err.Error() == "name_taken" {
 | 
			
		||||
			err = nil
 | 
			
		||||
			channels, err := api.GetChannels(false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("Could not retrieve channels")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			for _, archivedChannel := range channels {
 | 
			
		||||
				if archivedChannel.Name == channelName {
 | 
			
		||||
					if archivedChannel.IsArchived {
 | 
			
		||||
						err = api.UnarchiveChannel(archivedChannel.ID)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							fmt.Printf("Could not unarchive %s: %s\n", archivedChannel.ID, err)
 | 
			
		||||
							return
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					channel = &archivedChannel
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error setting test channel for pinning: %s\n", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	postToChannelID = channel.ID
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Posting as %s (%s) in channel %s\n", postAsUserName, postAsUserID, postToChannelID)
 | 
			
		||||
 | 
			
		||||
	// Post a message.
 | 
			
		||||
	postParams := slack.PostMessageParameters{}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage(postToChannelID, "Is this any good?", postParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error posting message: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Grab a reference to the message.
 | 
			
		||||
	msgRef := slack.NewRefToMessage(channelID, timestamp)
 | 
			
		||||
 | 
			
		||||
	// Add message pin to channel
 | 
			
		||||
	if err := api.AddPin(channelID, msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding pin: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all of the users pins.
 | 
			
		||||
	listPins, _, err := api.ListPins(channelID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error listing pins: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("All pins by %s...\n", authTest.User)
 | 
			
		||||
	for _, item := range listPins {
 | 
			
		||||
		fmt.Printf(" > Item type: %s\n", item.Type)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the pin.
 | 
			
		||||
	err = api.RemovePin(channelID, msgRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error remove pin: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = api.ArchiveChannel(channelID); err != nil {
 | 
			
		||||
		fmt.Printf("Error archiving channel: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								vendor/github.com/nlopes/slack/examples/reactions/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/nlopes/slack/examples/reactions/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		postAsUserName  string
 | 
			
		||||
		postAsUserID    string
 | 
			
		||||
		postToUserName  string
 | 
			
		||||
		postToUserID    string
 | 
			
		||||
		postToChannelID string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Find the user to post as.
 | 
			
		||||
	authTest, err := api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting channels: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Post as the authenticated user.
 | 
			
		||||
	postAsUserName = authTest.User
 | 
			
		||||
	postAsUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Posting to DM with self causes a conversation with slackbot.
 | 
			
		||||
	postToUserName = authTest.User
 | 
			
		||||
	postToUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Find the channel.
 | 
			
		||||
	_, _, chanID, err := api.OpenIMChannel(postToUserID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error opening IM: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	postToChannelID = chanID
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Posting as %s (%s) in DM with %s (%s), channel %s\n", postAsUserName, postAsUserID, postToUserName, postToUserID, postToChannelID)
 | 
			
		||||
 | 
			
		||||
	// Post a message.
 | 
			
		||||
	postParams := slack.PostMessageParameters{}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage(postToChannelID, "Is this any good?", postParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error posting message: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Grab a reference to the message.
 | 
			
		||||
	msgRef := slack.NewRefToMessage(channelID, timestamp)
 | 
			
		||||
 | 
			
		||||
	// React with :+1:
 | 
			
		||||
	if err := api.AddReaction("+1", msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// React with :-1:
 | 
			
		||||
	if err := api.AddReaction("cry", msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all reactions on the message.
 | 
			
		||||
	msgReactions, err := api.GetReactions(msgRef, slack.NewGetReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("%d reactions to message...\n", len(msgReactions))
 | 
			
		||||
	for _, r := range msgReactions {
 | 
			
		||||
		fmt.Printf("  %d users say %s\n", r.Count, r.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all of the users reactions.
 | 
			
		||||
	listReactions, _, err := api.ListReactions(slack.NewListReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error listing reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("All reactions by %s...\n", authTest.User)
 | 
			
		||||
	for _, item := range listReactions {
 | 
			
		||||
		fmt.Printf("%d on a %s...\n", len(item.Reactions), item.Type)
 | 
			
		||||
		for _, r := range item.Reactions {
 | 
			
		||||
			fmt.Printf("  %s (along with %d others)\n", r.Name, r.Count-1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the :cry: reaction.
 | 
			
		||||
	err = api.RemoveReaction("cry", msgRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error remove reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all reactions on the message.
 | 
			
		||||
	msgReactions, err = api.GetReactions(msgRef, slack.NewGetReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("%d reactions to message after removing cry...\n", len(msgReactions))
 | 
			
		||||
	for _, r := range msgReactions {
 | 
			
		||||
		fmt.Printf("  %d users say %s\n", r.Count, r.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/nlopes/slack/examples/stars/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/nlopes/slack/examples/stars/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all stars for the usr.
 | 
			
		||||
	params := slack.NewStarsParameters()
 | 
			
		||||
	starredItems, _, err := api.GetStarred(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting stars: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, s := range starredItems {
 | 
			
		||||
		var desc string
 | 
			
		||||
		switch s.Type {
 | 
			
		||||
		case slack.TYPE_MESSAGE:
 | 
			
		||||
			desc = s.Message.Text
 | 
			
		||||
		case slack.TYPE_FILE:
 | 
			
		||||
			desc = s.File.Name
 | 
			
		||||
		case slack.TYPE_FILE_COMMENT:
 | 
			
		||||
			desc = s.File.Name + " - " + s.Comment.Comment
 | 
			
		||||
		case slack.TYPE_CHANNEL, slack.TYPE_IM, slack.TYPE_GROUP:
 | 
			
		||||
			desc = s.Channel
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("Starred %s: %s\n", s.Type, desc)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/nlopes/slack/examples/users/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/nlopes/slack/examples/users/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	user, err := api.GetUserInfo("U023BECGF")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR TOKEN HERE")
 | 
			
		||||
	logger := log.New(os.Stdout, "slack-bot: ", log.Lshortfile|log.LstdFlags)
 | 
			
		||||
	slack.SetLogger(logger)
 | 
			
		||||
	api.SetDebug(true)
 | 
			
		||||
 | 
			
		||||
	rtm := api.NewRTM()
 | 
			
		||||
	go rtm.ManageConnection()
 | 
			
		||||
 | 
			
		||||
Loop:
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case msg := <-rtm.IncomingEvents:
 | 
			
		||||
			fmt.Print("Event Received: ")
 | 
			
		||||
			switch ev := msg.Data.(type) {
 | 
			
		||||
			case *slack.HelloEvent:
 | 
			
		||||
				// Ignore hello
 | 
			
		||||
 | 
			
		||||
			case *slack.ConnectedEvent:
 | 
			
		||||
				fmt.Println("Infos:", ev.Info)
 | 
			
		||||
				fmt.Println("Connection counter:", ev.ConnectionCount)
 | 
			
		||||
				// Replace #general with your Channel ID
 | 
			
		||||
				rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", "#general"))
 | 
			
		||||
 | 
			
		||||
			case *slack.MessageEvent:
 | 
			
		||||
				fmt.Printf("Message: %v\n", ev)
 | 
			
		||||
 | 
			
		||||
			case *slack.PresenceChangeEvent:
 | 
			
		||||
				fmt.Printf("Presence Change: %v\n", ev)
 | 
			
		||||
 | 
			
		||||
			case *slack.LatencyReport:
 | 
			
		||||
				fmt.Printf("Current latency: %v\n", ev.Value)
 | 
			
		||||
 | 
			
		||||
			case *slack.RTMError:
 | 
			
		||||
				fmt.Printf("Error: %s\n", ev.Error())
 | 
			
		||||
 | 
			
		||||
			case *slack.InvalidAuthEvent:
 | 
			
		||||
				fmt.Printf("Invalid credentials")
 | 
			
		||||
				break Loop
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
 | 
			
		||||
				// Ignore other events..
 | 
			
		||||
				// fmt.Printf("Unexpected: %v\n", msg.Data)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user