Compare commits
	
		
			50 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					50ac0fdd5d | ||
| 
						 | 
					2dfc1fada8 | ||
| 
						 | 
					979c7dde01 | ||
| 
						 | 
					be898b44c3 | ||
| 
						 | 
					4828c43443 | ||
| 
						 | 
					a5d0197349 | ||
| 
						 | 
					f1ed2ab403 | ||
| 
						 | 
					f8329d8c77 | ||
| 
						 | 
					ecf5669e80 | ||
| 
						 | 
					f825636c4f | ||
| 
						 | 
					ef8fbe1756 | ||
| 
						 | 
					612acfddff | ||
| 
						 | 
					932b80d4f7 | ||
| 
						 | 
					fac5f69ad2 | ||
| 
						 | 
					97c944bb63 | ||
| 
						 | 
					d0c4fe78ee | ||
| 
						 | 
					265457b451 | ||
| 
						 | 
					4a4a29c9f6 | ||
| 
						 | 
					0a91b9e1c9 | ||
| 
						 | 
					f56163295c | ||
| 
						 | 
					d1c87c068b | ||
| 
						 | 
					fa20761110 | ||
| 
						 | 
					e4a0e0a0e9 | ||
| 
						 | 
					d30ae19e2a | ||
| 
						 | 
					5c919e6bff | ||
| 
						 | 
					434393d1c3 | ||
| 
						 | 
					af9aa5d7cb | ||
| 
						 | 
					05eb75442a | ||
| 
						 | 
					3496ed0c7e | ||
| 
						 | 
					1b89604c7a | ||
| 
						 | 
					67a9d133e9 | ||
| 
						 | 
					ed9118b346 | ||
| 
						 | 
					59e55cfbd5 | ||
| 
						 | 
					788d3b32ac | ||
| 
						 | 
					1d414cf2fd | ||
| 
						 | 
					cc3c168162 | ||
| 
						 | 
					1ee6837f0e | ||
| 
						 | 
					27dcea7c5b | ||
| 
						 | 
					dcda7f7b8c | ||
| 
						 | 
					e0cbb69a4f | ||
| 
						 | 
					7ec95f786d | ||
| 
						 | 
					1efe40add5 | ||
| 
						 | 
					cbd73ee313 | ||
| 
						 | 
					34227a7a39 | ||
| 
						 | 
					71cb9b2d1d | ||
| 
						 | 
					cd4c9b194f | ||
| 
						 | 
					98762a0235 | ||
| 
						 | 
					2fd1fd9573 | ||
| 
						 | 
					aff3964078 | ||
| 
						 | 
					2778580397 | 
@@ -54,7 +54,7 @@ See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
# Installing
 | 
			
		||||
## Binaries
 | 
			
		||||
* Latest stable release [v1.4.1](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Latest stable release [v1.6.3](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)  
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
@@ -175,7 +175,7 @@ Matterbridge wouldn't exist without these libraries:
 | 
			
		||||
* echo - https://github.com/labstack/echo
 | 
			
		||||
* gitter - https://github.com/sromku/go-gitter
 | 
			
		||||
* gops - https://github.com/google/gops
 | 
			
		||||
* irc - https://github.com/thoj/go-ircevent
 | 
			
		||||
* irc - https://github.com/lrstanley/girc
 | 
			
		||||
* mattermost - https://github.com/mattermost/platform
 | 
			
		||||
* matrix - https://github.com/matrix-org/gomatrix
 | 
			
		||||
* slack - https://github.com/nlopes/slack
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
@@ -8,14 +9,13 @@ import (
 | 
			
		||||
	"github.com/zfjagann/golang-ring"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Api struct {
 | 
			
		||||
	Config   *config.Protocol
 | 
			
		||||
	Remote   chan config.Message
 | 
			
		||||
	Account  string
 | 
			
		||||
	Messages ring.Ring
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ApiMessage struct {
 | 
			
		||||
@@ -33,23 +33,21 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Api {
 | 
			
		||||
	b := &Api{}
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Api {
 | 
			
		||||
	b := &Api{BridgeConfig: cfg}
 | 
			
		||||
	e := echo.New()
 | 
			
		||||
	b.Messages = ring.Ring{}
 | 
			
		||||
	b.Messages.SetCapacity(cfg.Buffer)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Messages.SetCapacity(b.Config.Buffer)
 | 
			
		||||
	if b.Config.Token != "" {
 | 
			
		||||
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
 | 
			
		||||
			return key == b.Config.Token, nil
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
	e.GET("/api/messages", b.handleMessages)
 | 
			
		||||
	e.GET("/api/stream", b.handleStream)
 | 
			
		||||
	e.POST("/api/message", b.handlePostMessage)
 | 
			
		||||
	go func() {
 | 
			
		||||
		flog.Fatal(e.Start(cfg.BindAddress))
 | 
			
		||||
		flog.Fatal(e.Start(b.Config.BindAddress))
 | 
			
		||||
	}()
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
@@ -103,3 +101,24 @@ func (b *Api) handleMessages(c echo.Context) error {
 | 
			
		||||
	b.Messages = ring.Ring{}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Api) handleStream(c echo.Context) error {
 | 
			
		||||
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
 | 
			
		||||
	c.Response().WriteHeader(http.StatusOK)
 | 
			
		||||
	closeNotifier := c.Response().CloseNotify()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-closeNotifier:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			msg := b.Messages.Dequeue()
 | 
			
		||||
			if msg != nil {
 | 
			
		||||
				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				c.Response().Flush()
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(200 * time.Millisecond)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/mattermost"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/rocketchat"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/slack"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/sshchat"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/steam"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/telegram"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/xmpp"
 | 
			
		||||
@@ -45,44 +46,49 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Brid
 | 
			
		||||
	b.Protocol = protocol
 | 
			
		||||
	b.Account = bridge.Account
 | 
			
		||||
	b.Joined = make(map[string]bool)
 | 
			
		||||
	bridgeConfig := &config.BridgeConfig{General: &cfg.General, Account: bridge.Account, Remote: c}
 | 
			
		||||
 | 
			
		||||
	// override config from environment
 | 
			
		||||
	config.OverrideCfgFromEnv(cfg, protocol, name)
 | 
			
		||||
	switch protocol {
 | 
			
		||||
	case "mattermost":
 | 
			
		||||
		b.Config = cfg.Mattermost[name]
 | 
			
		||||
		b.Bridger = bmattermost.New(cfg.Mattermost[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Mattermost[name]
 | 
			
		||||
		b.Bridger = bmattermost.New(bridgeConfig)
 | 
			
		||||
	case "irc":
 | 
			
		||||
		b.Config = cfg.IRC[name]
 | 
			
		||||
		b.Bridger = birc.New(cfg.IRC[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.IRC[name]
 | 
			
		||||
		b.Bridger = birc.New(bridgeConfig)
 | 
			
		||||
	case "gitter":
 | 
			
		||||
		b.Config = cfg.Gitter[name]
 | 
			
		||||
		b.Bridger = bgitter.New(cfg.Gitter[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Gitter[name]
 | 
			
		||||
		b.Bridger = bgitter.New(bridgeConfig)
 | 
			
		||||
	case "slack":
 | 
			
		||||
		b.Config = cfg.Slack[name]
 | 
			
		||||
		b.Bridger = bslack.New(cfg.Slack[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Slack[name]
 | 
			
		||||
		b.Bridger = bslack.New(bridgeConfig)
 | 
			
		||||
	case "xmpp":
 | 
			
		||||
		b.Config = cfg.Xmpp[name]
 | 
			
		||||
		b.Bridger = bxmpp.New(cfg.Xmpp[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Xmpp[name]
 | 
			
		||||
		b.Bridger = bxmpp.New(bridgeConfig)
 | 
			
		||||
	case "discord":
 | 
			
		||||
		b.Config = cfg.Discord[name]
 | 
			
		||||
		b.Bridger = bdiscord.New(cfg.Discord[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Discord[name]
 | 
			
		||||
		b.Bridger = bdiscord.New(bridgeConfig)
 | 
			
		||||
	case "telegram":
 | 
			
		||||
		b.Config = cfg.Telegram[name]
 | 
			
		||||
		b.Bridger = btelegram.New(cfg.Telegram[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Telegram[name]
 | 
			
		||||
		b.Bridger = btelegram.New(bridgeConfig)
 | 
			
		||||
	case "rocketchat":
 | 
			
		||||
		b.Config = cfg.Rocketchat[name]
 | 
			
		||||
		b.Bridger = brocketchat.New(cfg.Rocketchat[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Rocketchat[name]
 | 
			
		||||
		b.Bridger = brocketchat.New(bridgeConfig)
 | 
			
		||||
	case "matrix":
 | 
			
		||||
		b.Config = cfg.Matrix[name]
 | 
			
		||||
		b.Bridger = bmatrix.New(cfg.Matrix[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Matrix[name]
 | 
			
		||||
		b.Bridger = bmatrix.New(bridgeConfig)
 | 
			
		||||
	case "steam":
 | 
			
		||||
		b.Config = cfg.Steam[name]
 | 
			
		||||
		b.Bridger = bsteam.New(cfg.Steam[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Steam[name]
 | 
			
		||||
		b.Bridger = bsteam.New(bridgeConfig)
 | 
			
		||||
	case "sshchat":
 | 
			
		||||
		bridgeConfig.Config = cfg.Sshchat[name]
 | 
			
		||||
		b.Bridger = bsshchat.New(bridgeConfig)
 | 
			
		||||
	case "api":
 | 
			
		||||
		b.Config = cfg.Api[name]
 | 
			
		||||
		b.Bridger = api.New(cfg.Api[name], bridge.Account, c)
 | 
			
		||||
		bridgeConfig.Config = cfg.Api[name]
 | 
			
		||||
		b.Bridger = api.New(bridgeConfig)
 | 
			
		||||
	}
 | 
			
		||||
	b.Config = bridgeConfig.Config
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ type FileInfo struct {
 | 
			
		||||
	Name    string
 | 
			
		||||
	Data    *[]byte
 | 
			
		||||
	Comment string
 | 
			
		||||
	URL     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChannelInfo struct {
 | 
			
		||||
@@ -59,41 +60,48 @@ type Protocol struct {
 | 
			
		||||
	IgnoreMessages         string // all protocols
 | 
			
		||||
	Jid                    string // xmpp
 | 
			
		||||
	Login                  string // mattermost, matrix
 | 
			
		||||
	Muc                    string // xmpp
 | 
			
		||||
	Name                   string // all protocols
 | 
			
		||||
	Nick                   string // all protocols
 | 
			
		||||
	NickFormatter          string // mattermost, slack
 | 
			
		||||
	NickServNick           string // IRC
 | 
			
		||||
	NickServUsername       string // IRC
 | 
			
		||||
	NickServPassword       string // IRC
 | 
			
		||||
	NicksPerRow            int    // mattermost, slack
 | 
			
		||||
	NoHomeServerSuffix     bool   // matrix
 | 
			
		||||
	NoTLS                  bool   // mattermost
 | 
			
		||||
	Password               string // IRC,mattermost,XMPP,matrix
 | 
			
		||||
	PrefixMessagesWithNick bool   // mattemost, slack
 | 
			
		||||
	Protocol               string //all protocols
 | 
			
		||||
	MessageQueue           int    // IRC, size of message queue for flood control
 | 
			
		||||
	MessageDelay           int    // IRC, time in millisecond to wait between messages
 | 
			
		||||
	MessageLength          int    // IRC, max length of a message allowed
 | 
			
		||||
	MessageFormat          string // telegram
 | 
			
		||||
	RemoteNickFormat       string // all protocols
 | 
			
		||||
	Server                 string // IRC,mattermost,XMPP,discord
 | 
			
		||||
	ShowJoinPart           bool   // all protocols
 | 
			
		||||
	ShowEmbeds             bool   // discord
 | 
			
		||||
	SkipTLSVerify          bool   // IRC, mattermost
 | 
			
		||||
	StripNick              bool   // all protocols
 | 
			
		||||
	Team                   string // mattermost
 | 
			
		||||
	Token                  string // gitter, slack, discord, api
 | 
			
		||||
	URL                    string // mattermost, slack // DEPRECATED
 | 
			
		||||
	UseAPI                 bool   // mattermost, slack
 | 
			
		||||
	UseSASL                bool   // IRC
 | 
			
		||||
	UseTLS                 bool   // IRC
 | 
			
		||||
	UseFirstName           bool   // telegram
 | 
			
		||||
	UseUserName            bool   // discord
 | 
			
		||||
	UseInsecureURL         bool   // telegram
 | 
			
		||||
	WebhookBindAddress     string // mattermost, slack
 | 
			
		||||
	WebhookURL             string // mattermost, slack
 | 
			
		||||
	WebhookUse             string // mattermost, slack, discord
 | 
			
		||||
	MediaDownloadSize      int    // all protocols
 | 
			
		||||
	MediaServerDownload    string
 | 
			
		||||
	MediaServerUpload      string
 | 
			
		||||
	MessageDelay           int        // IRC, time in millisecond to wait between messages
 | 
			
		||||
	MessageFormat          string     // telegram
 | 
			
		||||
	MessageLength          int        // IRC, max length of a message allowed
 | 
			
		||||
	MessageQueue           int        // IRC, size of message queue for flood control
 | 
			
		||||
	MessageSplit           bool       // IRC, split long messages with newlines on MessageLength instead of clipping
 | 
			
		||||
	Muc                    string     // xmpp
 | 
			
		||||
	Name                   string     // all protocols
 | 
			
		||||
	Nick                   string     // all protocols
 | 
			
		||||
	NickFormatter          string     // mattermost, slack
 | 
			
		||||
	NickServNick           string     // IRC
 | 
			
		||||
	NickServUsername       string     // IRC
 | 
			
		||||
	NickServPassword       string     // IRC
 | 
			
		||||
	NicksPerRow            int        // mattermost, slack
 | 
			
		||||
	NoHomeServerSuffix     bool       // matrix
 | 
			
		||||
	NoTLS                  bool       // mattermost
 | 
			
		||||
	Password               string     // IRC,mattermost,XMPP,matrix
 | 
			
		||||
	PrefixMessagesWithNick bool       // mattemost, slack
 | 
			
		||||
	Protocol               string     // all protocols
 | 
			
		||||
	RejoinDelay            int        // IRC
 | 
			
		||||
	ReplaceMessages        [][]string // all protocols
 | 
			
		||||
	ReplaceNicks           [][]string // all protocols
 | 
			
		||||
	RemoteNickFormat       string     // all protocols
 | 
			
		||||
	Server                 string     // IRC,mattermost,XMPP,discord
 | 
			
		||||
	ShowJoinPart           bool       // all protocols
 | 
			
		||||
	ShowEmbeds             bool       // discord
 | 
			
		||||
	SkipTLSVerify          bool       // IRC, mattermost
 | 
			
		||||
	StripNick              bool       // all protocols
 | 
			
		||||
	Team                   string     // mattermost
 | 
			
		||||
	Token                  string     // gitter, slack, discord, api
 | 
			
		||||
	URL                    string     // mattermost, slack // DEPRECATED
 | 
			
		||||
	UseAPI                 bool       // mattermost, slack
 | 
			
		||||
	UseSASL                bool       // IRC
 | 
			
		||||
	UseTLS                 bool       // IRC
 | 
			
		||||
	UseFirstName           bool       // telegram
 | 
			
		||||
	UseUserName            bool       // discord
 | 
			
		||||
	UseInsecureURL         bool       // telegram
 | 
			
		||||
	WebhookBindAddress     string     // mattermost, slack
 | 
			
		||||
	WebhookURL             string     // mattermost, slack
 | 
			
		||||
	WebhookUse             string     // mattermost, slack, discord
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChannelOptions struct {
 | 
			
		||||
@@ -135,11 +143,19 @@ type Config struct {
 | 
			
		||||
	Discord            map[string]Protocol
 | 
			
		||||
	Telegram           map[string]Protocol
 | 
			
		||||
	Rocketchat         map[string]Protocol
 | 
			
		||||
	Sshchat            map[string]Protocol
 | 
			
		||||
	General            Protocol
 | 
			
		||||
	Gateway            []Gateway
 | 
			
		||||
	SameChannelGateway []SameChannelGateway
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BridgeConfig struct {
 | 
			
		||||
	Config  Protocol
 | 
			
		||||
	General *Protocol
 | 
			
		||||
	Account string
 | 
			
		||||
	Remote  chan Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewConfig(cfgfile string) *Config {
 | 
			
		||||
	var cfg Config
 | 
			
		||||
	if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil {
 | 
			
		||||
@@ -167,6 +183,9 @@ func NewConfig(cfgfile string) *Config {
 | 
			
		||||
	if fail {
 | 
			
		||||
		log.Fatalf("Fix your config. Please see changelog for more information")
 | 
			
		||||
	}
 | 
			
		||||
	if cfg.General.MediaDownloadSize == 0 {
 | 
			
		||||
		cfg.General.MediaDownloadSize = 1000000
 | 
			
		||||
	}
 | 
			
		||||
	return &cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,6 @@ import (
 | 
			
		||||
 | 
			
		||||
type bdiscord struct {
 | 
			
		||||
	c              *discordgo.Session
 | 
			
		||||
	Config         *config.Protocol
 | 
			
		||||
	Remote         chan config.Message
 | 
			
		||||
	Account        string
 | 
			
		||||
	Channels       []*discordgo.Channel
 | 
			
		||||
	Nick           string
 | 
			
		||||
	UseChannelID   bool
 | 
			
		||||
@@ -24,6 +21,7 @@ type bdiscord struct {
 | 
			
		||||
	webhookToken   string
 | 
			
		||||
	channelInfoMap map[string]*config.ChannelInfo
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -33,11 +31,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *bdiscord {
 | 
			
		||||
	b := &bdiscord{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
func New(cfg *config.BridgeConfig) *bdiscord {
 | 
			
		||||
	b := &bdiscord{BridgeConfig: cfg}
 | 
			
		||||
	b.userMemberMap = make(map[string]*discordgo.Member)
 | 
			
		||||
	b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
			
		||||
	if b.Config.WebhookURL != "" {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,13 +9,11 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bgitter struct {
 | 
			
		||||
	c       *gitter.Gitter
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	User    *gitter.User
 | 
			
		||||
	Users   []gitter.User
 | 
			
		||||
	Rooms   []gitter.Room
 | 
			
		||||
	c     *gitter.Gitter
 | 
			
		||||
	User  *gitter.User
 | 
			
		||||
	Users []gitter.User
 | 
			
		||||
	Rooms []gitter.Room
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -25,12 +23,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bgitter {
 | 
			
		||||
	b := &Bgitter{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bgitter {
 | 
			
		||||
	return &Bgitter{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bgitter) Connect() error {
 | 
			
		||||
@@ -125,6 +119,23 @@ func (b *Bgitter) Send(msg config.Message) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				_, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
 
 | 
			
		||||
@@ -26,3 +26,15 @@ func DownloadFile(url string) (*[]byte, error) {
 | 
			
		||||
	resp.Body.Close()
 | 
			
		||||
	return &data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SplitStringLength(input string, length int) string {
 | 
			
		||||
	a := []rune(input)
 | 
			
		||||
	str := ""
 | 
			
		||||
	for i, r := range a {
 | 
			
		||||
		str = str + string(r)
 | 
			
		||||
		if i > 0 && (i+1)%length == 0 {
 | 
			
		||||
			str += "\n"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/lrstanley/girc"
 | 
			
		||||
	"github.com/paulrosania/go-charset/charset"
 | 
			
		||||
@@ -24,12 +25,11 @@ type Birc struct {
 | 
			
		||||
	i               *girc.Client
 | 
			
		||||
	Nick            string
 | 
			
		||||
	names           map[string][]string
 | 
			
		||||
	Config          *config.Protocol
 | 
			
		||||
	Remote          chan config.Message
 | 
			
		||||
	connected       chan struct{}
 | 
			
		||||
	Local           chan config.Message // local queue for flood control
 | 
			
		||||
	Account         string
 | 
			
		||||
	FirstConnection bool
 | 
			
		||||
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -39,13 +39,11 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Birc {
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Birc {
 | 
			
		||||
	b := &Birc{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.BridgeConfig = cfg
 | 
			
		||||
	b.Nick = b.Config.Nick
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.names = make(map[string][]string)
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.connected = make(chan struct{})
 | 
			
		||||
	if b.Config.MessageDelay == 0 {
 | 
			
		||||
		b.Config.MessageDelay = 1300
 | 
			
		||||
@@ -178,9 +176,27 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
			
		||||
		msg.Text = buf.String()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// split long messages on messageLength, to avoid clipped messages #281
 | 
			
		||||
	if b.Config.MessageSplit {
 | 
			
		||||
		msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength)
 | 
			
		||||
	}
 | 
			
		||||
	for _, text := range strings.Split(msg.Text, "\n") {
 | 
			
		||||
		input := []rune(text)
 | 
			
		||||
		if len(text) > b.Config.MessageLength {
 | 
			
		||||
			text = text[:b.Config.MessageLength] + " <message clipped>"
 | 
			
		||||
			text = string(input[:b.Config.MessageLength]) + " <message clipped>"
 | 
			
		||||
		}
 | 
			
		||||
		if len(b.Local) < b.Config.MessageQueue {
 | 
			
		||||
			if len(b.Local) == b.Config.MessageQueue-1 {
 | 
			
		||||
@@ -251,6 +267,7 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
 | 
			
		||||
	channel := event.Params[0]
 | 
			
		||||
	if event.Command == "KICK" {
 | 
			
		||||
		flog.Infof("Got kicked from %s by %s", channel, event.Source.Name)
 | 
			
		||||
		time.Sleep(time.Duration(b.Config.RejoinDelay) * time.Second)
 | 
			
		||||
		b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: channel, Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -306,14 +323,13 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
 | 
			
		||||
	if event.Source.Name == b.Nick {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
			
		||||
	rmsg := config.Message{Username: event.Source.Name, Channel: strings.ToLower(event.Params[0]), Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
			
		||||
	flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event)
 | 
			
		||||
	msg := ""
 | 
			
		||||
	if event.Command == "CTCP_ACTION" {
 | 
			
		||||
		//	msg = event.Source.Name + " "
 | 
			
		||||
	if event.IsAction() {
 | 
			
		||||
		rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
	}
 | 
			
		||||
	msg += event.Trailing
 | 
			
		||||
	msg += event.StripAction()
 | 
			
		||||
	// strip IRC colors
 | 
			
		||||
	re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
 | 
			
		||||
	msg = re.ReplaceAllString(msg, "")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,24 @@
 | 
			
		||||
package bmatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"mime"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	matrix "github.com/matrix-org/gomatrix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bmatrix struct {
 | 
			
		||||
	mc      *matrix.Client
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	UserID  string
 | 
			
		||||
	RoomMap map[string]string
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -26,12 +28,9 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmatrix {
 | 
			
		||||
	b := &Bmatrix{}
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bmatrix {
 | 
			
		||||
	b := &Bmatrix{BridgeConfig: cfg}
 | 
			
		||||
	b.RoomMap = make(map[string]string)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -87,6 +86,44 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
			matrix.TextMessage{"m.emote", msg.Username + msg.Text})
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				content := bytes.NewReader(*fi.Data)
 | 
			
		||||
				sp := strings.Split(fi.Name, ".")
 | 
			
		||||
				mtype := mime.TypeByExtension("." + sp[len(sp)-1])
 | 
			
		||||
				if strings.Contains(mtype, "image") ||
 | 
			
		||||
					strings.Contains(mtype, "video") {
 | 
			
		||||
					flog.Debugf("uploading file: %s %s", fi.Name, mtype)
 | 
			
		||||
					res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("file upload failed: %#v", err)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					if strings.Contains(mtype, "video") {
 | 
			
		||||
						flog.Debugf("sendVideo %s", res.ContentURI)
 | 
			
		||||
						_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							flog.Errorf("sendVideo failed: %#v", err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if strings.Contains(mtype, "image") {
 | 
			
		||||
						flog.Debugf("sendImage %s", res.ContentURI)
 | 
			
		||||
						_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							flog.Errorf("sendImage failed: %#v", err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					flog.Debugf("result: %#v", res)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
@@ -104,7 +141,13 @@ func (b *Bmatrix) getRoomID(channel string) string {
 | 
			
		||||
func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
	syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
 | 
			
		||||
	syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
 | 
			
		||||
		if (ev.Content["msgtype"].(string) == "m.text" || ev.Content["msgtype"].(string) == "m.notice" || ev.Content["msgtype"].(string) == "m.emote") && ev.Sender != b.UserID {
 | 
			
		||||
		flog.Debugf("Received: %#v", ev)
 | 
			
		||||
		if (ev.Content["msgtype"].(string) == "m.text" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.notice" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.emote" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.file" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.video") && ev.Sender != b.UserID {
 | 
			
		||||
			b.RLock()
 | 
			
		||||
			channel, ok := b.RoomMap[ev.RoomID]
 | 
			
		||||
			b.RUnlock()
 | 
			
		||||
@@ -121,10 +164,31 @@ func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
			if ev.Content["msgtype"].(string) == "m.emote" {
 | 
			
		||||
				rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
			}
 | 
			
		||||
			if ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
				ev.Content["msgtype"].(string) == "m.video" ||
 | 
			
		||||
				ev.Content["msgtype"].(string) == "m.file" {
 | 
			
		||||
				flog.Debugf("ev: %#v", ev)
 | 
			
		||||
				rmsg.Extra = make(map[string][]interface{})
 | 
			
		||||
				url := ev.Content["url"].(string)
 | 
			
		||||
				url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1)
 | 
			
		||||
				info := ev.Content["info"].(map[string]interface{})
 | 
			
		||||
				size := info["size"].(float64)
 | 
			
		||||
				name := ev.Content["body"].(string)
 | 
			
		||||
				flog.Debugf("trying to download %#v with size %#v", name, size)
 | 
			
		||||
				if size <= float64(b.General.MediaDownloadSize) {
 | 
			
		||||
					data, err := helper.DownloadFile(url)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("download %s failed %#v", url, err)
 | 
			
		||||
					} else {
 | 
			
		||||
						flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url))
 | 
			
		||||
						rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{Name: name, Data: data})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				rmsg.Text = ""
 | 
			
		||||
			}
 | 
			
		||||
			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
 | 
			
		||||
			b.Remote <- rmsg
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debugf("Received: %#v", ev)
 | 
			
		||||
	})
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,10 +32,8 @@ type MMMessage struct {
 | 
			
		||||
type Bmattermost struct {
 | 
			
		||||
	MMhook
 | 
			
		||||
	MMapi
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	TeamId  string
 | 
			
		||||
	Account string
 | 
			
		||||
	TeamId string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -45,11 +43,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{BridgeConfig: cfg}
 | 
			
		||||
	b.mmMap = make(map[string]string)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,7 @@ type MMhook struct {
 | 
			
		||||
 | 
			
		||||
type Brocketchat struct {
 | 
			
		||||
	MMhook
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -26,12 +24,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Brocketchat {
 | 
			
		||||
	b := &Brocketchat{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Brocketchat {
 | 
			
		||||
	return &Brocketchat{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Brocketchat) Command(cmd string) string {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/matterbridge/slack"
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
	"html"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -27,14 +27,12 @@ type MMMessage struct {
 | 
			
		||||
type Bslack struct {
 | 
			
		||||
	mh       *matterhook.Client
 | 
			
		||||
	sc       *slack.Client
 | 
			
		||||
	Config   *config.Protocol
 | 
			
		||||
	rtm      *slack.RTM
 | 
			
		||||
	Plus     bool
 | 
			
		||||
	Remote   chan config.Message
 | 
			
		||||
	Users    []slack.User
 | 
			
		||||
	Account  string
 | 
			
		||||
	si       *slack.Info
 | 
			
		||||
	channels []slack.Channel
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -44,12 +42,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bslack {
 | 
			
		||||
	b := &Bslack{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bslack {
 | 
			
		||||
	return &Bslack{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) Command(cmd string) string {
 | 
			
		||||
@@ -113,7 +107,7 @@ func (b *Bslack) Disconnect() error {
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
	// we can only join channels using the API
 | 
			
		||||
	if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" {
 | 
			
		||||
	if b.sc != nil {
 | 
			
		||||
		if strings.HasPrefix(b.Config.Token, "xoxb") {
 | 
			
		||||
			// TODO check if bot has already joined channel
 | 
			
		||||
			return nil
 | 
			
		||||
@@ -161,7 +155,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
		np.AsUser = true
 | 
			
		||||
	}
 | 
			
		||||
	np.Username = nick
 | 
			
		||||
	np.IconURL = config.GetIconURL(&msg, b.Config)
 | 
			
		||||
	np.IconURL = config.GetIconURL(&msg, &b.Config)
 | 
			
		||||
	if msg.Avatar != "" {
 | 
			
		||||
		np.IconURL = msg.Avatar
 | 
			
		||||
	}
 | 
			
		||||
@@ -294,7 +288,7 @@ func (b *Bslack) handleSlack() {
 | 
			
		||||
		// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
			
		||||
		if message.Raw.File != nil {
 | 
			
		||||
			// limit to 1MB for now
 | 
			
		||||
			if message.Raw.File.Size <= 1000000 {
 | 
			
		||||
			if message.Raw.File.Size <= b.General.MediaDownloadSize {
 | 
			
		||||
				comment := ""
 | 
			
		||||
				data, err := b.downloadFile(message.Raw.File.URLPrivateDownload)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -315,9 +309,11 @@ func (b *Bslack) handleSlack() {
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
	for msg := range b.rtm.IncomingEvents {
 | 
			
		||||
		if msg.Type != "user_typing" && msg.Type != "latency_report" {
 | 
			
		||||
			flog.Debugf("Receiving from slackclient %#v", msg.Data)
 | 
			
		||||
		}
 | 
			
		||||
		switch ev := msg.Data.(type) {
 | 
			
		||||
		case *slack.MessageEvent:
 | 
			
		||||
			flog.Debugf("Receiving from slackclient %#v", ev)
 | 
			
		||||
			if len(ev.Attachments) > 0 {
 | 
			
		||||
				// skip messages we made ourselves
 | 
			
		||||
				if ev.Attachments[0].CallbackID == "matterbridge" {
 | 
			
		||||
@@ -400,6 +396,8 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
			}
 | 
			
		||||
		case *slack.InvalidAuthEvent:
 | 
			
		||||
			flog.Fatalf("Invalid Token %#v", ev)
 | 
			
		||||
		case *slack.ConnectionErrorEvent:
 | 
			
		||||
			flog.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								bridge/sshchat/sshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								bridge/sshchat/sshchat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
package bsshchat
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/shazow/ssh-chat/sshd"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bsshchat struct {
 | 
			
		||||
	r *bufio.Scanner
 | 
			
		||||
	w io.WriteCloser
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "sshchat"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bsshchat {
 | 
			
		||||
	return &Bsshchat{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) Connect() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	flog.Infof("Connecting %s", b.Config.Server)
 | 
			
		||||
	go func() {
 | 
			
		||||
		err = sshd.ConnectShell(b.Config.Server, b.Config.Nick, func(r io.Reader, w io.WriteCloser) error {
 | 
			
		||||
			b.r = bufio.NewScanner(r)
 | 
			
		||||
			b.w = w
 | 
			
		||||
			b.r.Scan()
 | 
			
		||||
			w.Write([]byte("/theme mono\r\n"))
 | 
			
		||||
			b.handleSshChat()
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
	}()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Debugf("%#v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	flog.Info("Connection succeeded")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) Disconnect() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) Send(msg config.Message) (string, error) {
 | 
			
		||||
	// ignore delete messages
 | 
			
		||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.w.Write([]byte(msg.Username + msg.Text))
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b.w.Write([]byte(msg.Username + msg.Text + "\r\n"))
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
func (b *Bsshchat) sshchatKeepAlive() chan bool {
 | 
			
		||||
	done := make(chan bool)
 | 
			
		||||
	go func() {
 | 
			
		||||
		ticker := time.NewTicker(90 * time.Second)
 | 
			
		||||
		defer ticker.Stop()
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-ticker.C:
 | 
			
		||||
				flog.Debugf("PING")
 | 
			
		||||
				err := b.xc.PingC2S("", "")
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					flog.Debugf("PING failed %#v", err)
 | 
			
		||||
				}
 | 
			
		||||
			case <-done:
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return done
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
func stripPrompt(s string) string {
 | 
			
		||||
	pos := strings.LastIndex(s, "\033[K")
 | 
			
		||||
	if pos < 0 {
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
	return s[pos+3:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bsshchat) handleSshChat() error {
 | 
			
		||||
	/*
 | 
			
		||||
		done := b.sshchatKeepAlive()
 | 
			
		||||
		defer close(done)
 | 
			
		||||
	*/
 | 
			
		||||
	wait := true
 | 
			
		||||
	for {
 | 
			
		||||
		if b.r.Scan() {
 | 
			
		||||
			res := strings.Split(stripPrompt(b.r.Text()), ":")
 | 
			
		||||
			if res[0] == "-> Set theme" {
 | 
			
		||||
				wait = false
 | 
			
		||||
				log.Debugf("mono found, allowing")
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !wait {
 | 
			
		||||
				flog.Debugf("message %#v", res)
 | 
			
		||||
				rmsg := config.Message{Username: res[0], Text: strings.Join(res[1:], ":"), Channel: "sshchat", Account: b.Account, UserID: "nick"}
 | 
			
		||||
				b.Remote <- rmsg
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -16,11 +16,9 @@ import (
 | 
			
		||||
type Bsteam struct {
 | 
			
		||||
	c         *steam.Client
 | 
			
		||||
	connected chan struct{}
 | 
			
		||||
	Config    *config.Protocol
 | 
			
		||||
	Remote    chan config.Message
 | 
			
		||||
	Account   string
 | 
			
		||||
	userMap   map[steamid.SteamId]string
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -30,11 +28,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bsteam {
 | 
			
		||||
	b := &Bsteam{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bsteam {
 | 
			
		||||
	b := &Bsteam{BridgeConfig: cfg}
 | 
			
		||||
	b.userMap = make(map[steamid.SteamId]string)
 | 
			
		||||
	b.connected = make(chan struct{})
 | 
			
		||||
	return b
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Btelegram struct {
 | 
			
		||||
	c       *tgbotapi.BotAPI
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	c *tgbotapi.BotAPI
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -25,12 +23,8 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Btelegram {
 | 
			
		||||
	b := &Btelegram{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	return b
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Btelegram {
 | 
			
		||||
	return &Btelegram{BridgeConfig: cfg}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Btelegram) Connect() error {
 | 
			
		||||
@@ -90,6 +84,9 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		m := tgbotapi.NewEditMessageText(chatid, msgid, msg.Username+msg.Text)
 | 
			
		||||
		if b.Config.MessageFormat == "HTML" {
 | 
			
		||||
			m.ParseMode = tgbotapi.ModeHTML
 | 
			
		||||
		}
 | 
			
		||||
		_, err = b.c.Send(m)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
@@ -179,6 +176,29 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
		if message.Document != nil {
 | 
			
		||||
			b.handleDownload(message.Document, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Voice != nil {
 | 
			
		||||
			b.handleDownload(message.Voice, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Audio != nil {
 | 
			
		||||
			b.handleDownload(message.Audio, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if message.ForwardFrom != nil {
 | 
			
		||||
			usernameForward := ""
 | 
			
		||||
			if b.Config.UseFirstName {
 | 
			
		||||
				usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
			}
 | 
			
		||||
			if usernameForward == "" {
 | 
			
		||||
				usernameForward = message.ForwardFrom.UserName
 | 
			
		||||
				if usernameForward == "" {
 | 
			
		||||
					usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if usernameForward == "" {
 | 
			
		||||
				usernameForward = "unknown"
 | 
			
		||||
			}
 | 
			
		||||
			text = "Forwarded from " + usernameForward + ": " + text
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// quote the previous message
 | 
			
		||||
		if message.ReplyToMessage != nil {
 | 
			
		||||
@@ -224,11 +244,31 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	text := ""
 | 
			
		||||
	fileid := ""
 | 
			
		||||
	switch v := file.(type) {
 | 
			
		||||
	case *tgbotapi.Audio:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *tgbotapi.Voice:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		if !strings.HasSuffix(name, ".ogg") {
 | 
			
		||||
			name = name + ".ogg"
 | 
			
		||||
		}
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *tgbotapi.Sticker:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		if !strings.HasSuffix(name, ".webp") {
 | 
			
		||||
			name = name + ".webp"
 | 
			
		||||
		}
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *tgbotapi.Video:
 | 
			
		||||
@@ -259,7 +299,7 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
			
		||||
	// limit to 1MB for now
 | 
			
		||||
	flog.Debugf("trying to download %#v fileid %#v with size %#v", name, fileid, size)
 | 
			
		||||
	if size <= 1000000 {
 | 
			
		||||
	if size <= b.General.MediaDownloadSize {
 | 
			
		||||
		data, err := helper.DownloadFile(url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Errorf("download %s failed %#v", url, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,7 @@ import (
 | 
			
		||||
type Bxmpp struct {
 | 
			
		||||
	xc      *xmpp.Client
 | 
			
		||||
	xmppMap map[string]string
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
@@ -26,12 +24,9 @@ func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{}
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{BridgeConfig: cfg}
 | 
			
		||||
	b.xmppMap = make(map[string]string)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -85,6 +80,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,50 @@
 | 
			
		||||
# v1.6.3
 | 
			
		||||
## Bugfix
 | 
			
		||||
* slack: Fix connection issues
 | 
			
		||||
* slack: Add more debug messages
 | 
			
		||||
* irc: Convert received IRC channel names to lowercase. Fixes #329 (#330)
 | 
			
		||||
 | 
			
		||||
# v1.6.2
 | 
			
		||||
## Bugfix
 | 
			
		||||
* mattermost: Crashes while connecting to Mattermost (regression). Closes #327
 | 
			
		||||
 | 
			
		||||
# v1.6.1
 | 
			
		||||
## Bugfix
 | 
			
		||||
* general: Display of nicks not longer working (regression). Closes #323
 | 
			
		||||
 | 
			
		||||
# v1.6.0
 | 
			
		||||
## New features
 | 
			
		||||
* sshchat: New protocol support added (https://github.com/shazow/ssh-chat)
 | 
			
		||||
* general: Allow specifying maximum download size of media using MediaDownloadSize (slack,telegram,matrix)
 | 
			
		||||
* api: Add (simple, one listener) long-polling support (api). Closes #307
 | 
			
		||||
* telegram: Add support for forwarded messages. Closes #313
 | 
			
		||||
* telegram: Add support for Audio/Voice files (telegram). Closes #314
 | 
			
		||||
* irc: Add RejoinDelay option. Delay to rejoin after channel kick (irc). Closes #322
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: Also use HTML in edited messages (telegram). Closes #315
 | 
			
		||||
* matrix: Fix panic (matrix). Closes #316
 | 
			
		||||
 | 
			
		||||
# v1.5.1
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* irc: Fix irc ACTION regression (irc). Closes #306
 | 
			
		||||
* irc: Split on UTF-8 for MessageSplit (irc). Closes #308
 | 
			
		||||
 | 
			
		||||
# v1.5.0
 | 
			
		||||
## New features
 | 
			
		||||
* general: remote mediaserver support. See MediaServerDownload and MediaServerUpload in matterbridge.toml.sample
 | 
			
		||||
  more information on https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
 | 
			
		||||
* general: Add support for ReplaceNicks using regexp to replace nicks. Closes #269 (see matterbridge.toml.sample)
 | 
			
		||||
* general: Add support for ReplaceMessages using regexp to replace messages. #269 (see matterbridge.toml.sample)
 | 
			
		||||
* irc: Add MessageSplit option to split messages on MessageLength (irc). Closes #281
 | 
			
		||||
* matrix: Add support for uploading images/video (matrix). Closes #302
 | 
			
		||||
* matrix: Add support for uploaded images/video (matrix) 
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: Add webp extension to stickers if necessary (telegram)
 | 
			
		||||
* mattermost: Break when re-login fails (mattermost)
 | 
			
		||||
 | 
			
		||||
# v1.4.1
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: fix issue with uploading for images/documents/stickers
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
package gateway
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	//	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"github.com/hashicorp/golang-lru"
 | 
			
		||||
	"github.com/peterhellberg/emojilib"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -155,7 +158,8 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
		if dest.Protocol != "discord" &&
 | 
			
		||||
			dest.Protocol != "slack" &&
 | 
			
		||||
			dest.Protocol != "mattermost" &&
 | 
			
		||||
			dest.Protocol != "telegram" {
 | 
			
		||||
			dest.Protocol != "telegram" &&
 | 
			
		||||
			dest.Protocol != "matrix" {
 | 
			
		||||
			if msg.Text == "" {
 | 
			
		||||
				return brMsgIDs
 | 
			
		||||
			}
 | 
			
		||||
@@ -254,6 +258,20 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
 | 
			
		||||
	if nick == "" {
 | 
			
		||||
		nick = gw.Config.General.RemoteNickFormat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// loop to replace nicks
 | 
			
		||||
	for _, outer := range br.Config.ReplaceNicks {
 | 
			
		||||
		search := outer[0]
 | 
			
		||||
		replace := outer[1]
 | 
			
		||||
		// TODO move compile to bridge init somewhere
 | 
			
		||||
		re, err := regexp.Compile(search)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		msg.Username = re.ReplaceAllString(msg.Username, replace)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(msg.Username) > 0 {
 | 
			
		||||
		// fix utf-8 issue #193
 | 
			
		||||
		i := 0
 | 
			
		||||
@@ -287,9 +305,50 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string
 | 
			
		||||
func (gw *Gateway) modifyMessage(msg *config.Message) {
 | 
			
		||||
	// replace :emoji: to unicode
 | 
			
		||||
	msg.Text = emojilib.Replace(msg.Text)
 | 
			
		||||
	br := gw.Bridges[msg.Account]
 | 
			
		||||
	// loop to replace messages
 | 
			
		||||
	for _, outer := range br.Config.ReplaceMessages {
 | 
			
		||||
		search := outer[0]
 | 
			
		||||
		replace := outer[1]
 | 
			
		||||
		// TODO move compile to bridge init somewhere
 | 
			
		||||
		re, err := regexp.Compile(search)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		msg.Text = re.ReplaceAllString(msg.Text, replace)
 | 
			
		||||
	}
 | 
			
		||||
	msg.Gateway = gw.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
			
		||||
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
		client := &http.Client{
 | 
			
		||||
			Timeout: time.Second * 5,
 | 
			
		||||
		}
 | 
			
		||||
		for i, f := range msg.Extra["file"] {
 | 
			
		||||
			fi := f.(config.FileInfo)
 | 
			
		||||
			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
 | 
			
		||||
			reader := bytes.NewReader(*fi.Data)
 | 
			
		||||
			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
 | 
			
		||||
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
 | 
			
		||||
			extra := msg.Extra["file"][i].(config.FileInfo)
 | 
			
		||||
			extra.URL = durl
 | 
			
		||||
			msg.Extra["file"][i] = extra
 | 
			
		||||
			req, _ := http.NewRequest("PUT", url, reader)
 | 
			
		||||
			req.Header.Set("Content-Type", "binary/octet-stream")
 | 
			
		||||
			_, err := client.Do(req)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Errorf("mediaserver upload failed: %#v", err)
 | 
			
		||||
			}
 | 
			
		||||
			log.Debugf("mediaserver download URL = %s", durl)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getChannelID(msg config.Message) string {
 | 
			
		||||
	return msg.Channel + msg.Account
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,7 @@ func (r *Router) handleReceive() {
 | 
			
		||||
			if !gw.ignoreMessage(&msg) {
 | 
			
		||||
				msg.Timestamp = time.Now()
 | 
			
		||||
				gw.modifyMessage(&msg)
 | 
			
		||||
				gw.handleFiles(&msg)
 | 
			
		||||
				for _, br := range gw.Bridges {
 | 
			
		||||
					msgIDs = append(msgIDs, gw.handleMessage(msg, br)...)
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.4.1"
 | 
			
		||||
	version = "1.6.3"
 | 
			
		||||
	githash string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,15 @@ MessageQueue=30
 | 
			
		||||
#OPTIONAL (default 400)
 | 
			
		||||
MessageLength=400
 | 
			
		||||
 | 
			
		||||
#Split messages on MessageLength instead of showing the <message clipped>
 | 
			
		||||
#WARNING: this could lead to flooding
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
MessageSplit=false
 | 
			
		||||
 | 
			
		||||
#Delay in seconds to rejoin a channel when kicked
 | 
			
		||||
#OPTIONAL (default 0)
 | 
			
		||||
RejoinDelay=0
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore. 
 | 
			
		||||
#Messages from those users will not be sent to other bridges.
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
@@ -91,6 +100,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -154,6 +180,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#Messages you want to replace.
 | 
			
		||||
#It replaces outgoing messages from the bridge.
 | 
			
		||||
#So you need to place it by the sending bridge definition.
 | 
			
		||||
#Regular expressions supported
 | 
			
		||||
#Some examples:
 | 
			
		||||
#This replaces cat => dog and sleep => awake
 | 
			
		||||
#ReplaceMessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#This Replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#ReplaceMessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#Nicks you want to replace.
 | 
			
		||||
#See ReplaceMessages for syntaxA
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -208,6 +251,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -322,6 +382,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -366,6 +443,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -457,6 +551,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -525,6 +636,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -592,6 +720,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -660,6 +805,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -720,6 +882,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -774,6 +953,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -838,6 +1034,30 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#MediaServerUpload and MediaServerDownload are used for uploading images/files/video to
 | 
			
		||||
#a remote "mediaserver" (a webserver like caddy for example).
 | 
			
		||||
#When configured images/files uploaded on bridges like mattermost,slack, telegram will be downloaded
 | 
			
		||||
#and uploaded again to MediaServerUpload URL
 | 
			
		||||
#The MediaServerDownload will be used so that bridges without native uploading support:
 | 
			
		||||
#gitter, irc and xmpp will be shown links to the files on MediaServerDownload
 | 
			
		||||
#
 | 
			
		||||
#More information https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
MediaServerUpload="https://user:pass@yourserver.com/upload"
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
MediaServerDownload="https://youserver.com/download"
 | 
			
		||||
 | 
			
		||||
#MediaDownloadSize is the maximum size of attachments, videos, images
 | 
			
		||||
#matterbridge will download and upload this file to bridges that also support uploading files.
 | 
			
		||||
#eg downloading from slack to upload it to mattermost
 | 
			
		||||
#
 | 
			
		||||
#It will only download from bridges that don't have public links available, which are for the moment
 | 
			
		||||
#slack, telegram and matrix
 | 
			
		||||
#
 | 
			
		||||
#Optional (default 1000000 (1 megabyte))
 | 
			
		||||
MediaDownloadSize=1000000
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#Gateway configuration
 | 
			
		||||
###################################################################
 | 
			
		||||
 
 | 
			
		||||
@@ -817,9 +817,14 @@ func (m *MMClient) StatusLoop() {
 | 
			
		||||
				backoff = time.Second * 60
 | 
			
		||||
			case <-time.After(time.Second * 5):
 | 
			
		||||
				if retries > 3 {
 | 
			
		||||
					m.log.Debug("StatusLoop() timeout")
 | 
			
		||||
					m.Logout()
 | 
			
		||||
					m.WsQuit = false
 | 
			
		||||
					m.Login()
 | 
			
		||||
					err := m.Login()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						log.Errorf("Login failed: %#v", err)
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					if m.OnWsConnect != nil {
 | 
			
		||||
						m.OnWsConnect()
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -494,14 +494,9 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *context) File(file string) (err error) {
 | 
			
		||||
	file, err = url.QueryUnescape(file) // Issue #839
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open(file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
		return NotFoundHandler(c)
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
@@ -510,7 +505,7 @@ func (c *context) File(file string) (err error) {
 | 
			
		||||
		file = filepath.Join(file, indexPage)
 | 
			
		||||
		f, err = os.Open(file)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return ErrNotFound
 | 
			
		||||
			return NotFoundHandler(c)
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		if fi, err = f.Stat(); err != nil {
 | 
			
		||||
@@ -530,7 +525,7 @@ func (c *context) Inline(file, name string) (err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *context) contentDisposition(file, name, dispositionType string) (err error) {
 | 
			
		||||
	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%s", dispositionType, name))
 | 
			
		||||
	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name))
 | 
			
		||||
	c.File(file)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -72,6 +72,7 @@ type (
 | 
			
		||||
		TLSServer        *http.Server
 | 
			
		||||
		Listener         net.Listener
 | 
			
		||||
		TLSListener      net.Listener
 | 
			
		||||
		AutoTLSManager   autocert.Manager
 | 
			
		||||
		DisableHTTP2     bool
 | 
			
		||||
		Debug            bool
 | 
			
		||||
		HideBanner       bool
 | 
			
		||||
@@ -79,22 +80,22 @@ type (
 | 
			
		||||
		Binder           Binder
 | 
			
		||||
		Validator        Validator
 | 
			
		||||
		Renderer         Renderer
 | 
			
		||||
		AutoTLSManager   autocert.Manager
 | 
			
		||||
		// Mutex            sync.RWMutex
 | 
			
		||||
		Logger Logger
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Route contains a handler and information for matching against requests.
 | 
			
		||||
	Route struct {
 | 
			
		||||
		Method  string `json:"method"`
 | 
			
		||||
		Path    string `json:"path"`
 | 
			
		||||
		Handler string `json:"handler"`
 | 
			
		||||
		Method string `json:"method"`
 | 
			
		||||
		Path   string `json:"path"`
 | 
			
		||||
		Name   string `json:"name"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// HTTPError represents an error that occurred while handling a request.
 | 
			
		||||
	HTTPError struct {
 | 
			
		||||
		Code    int
 | 
			
		||||
		Message interface{}
 | 
			
		||||
		Inner   error // Stores the error returned by an external dependency
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// MiddlewareFunc defines a function to process middleware.
 | 
			
		||||
@@ -121,7 +122,7 @@ type (
 | 
			
		||||
 | 
			
		||||
	// i is the interface for Echo and Group.
 | 
			
		||||
	i interface {
 | 
			
		||||
		GET(string, HandlerFunc, ...MiddlewareFunc)
 | 
			
		||||
		GET(string, HandlerFunc, ...MiddlewareFunc) *Route
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -212,7 +213,7 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	version = "3.1.0"
 | 
			
		||||
	version = "3.2.5"
 | 
			
		||||
	website = "https://echo.labstack.com"
 | 
			
		||||
	// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
 | 
			
		||||
	banner = `
 | 
			
		||||
@@ -282,7 +283,7 @@ func New() (e *Echo) {
 | 
			
		||||
	e.TLSServer.Handler = e
 | 
			
		||||
	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
 | 
			
		||||
	e.Binder = &DefaultBinder{}
 | 
			
		||||
	e.Logger.SetLevel(log.OFF)
 | 
			
		||||
	e.Logger.SetLevel(log.ERROR)
 | 
			
		||||
	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
 | 
			
		||||
	e.pool.New = func() interface{} {
 | 
			
		||||
		return e.NewContext(nil, nil)
 | 
			
		||||
@@ -319,6 +320,9 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
 | 
			
		||||
	if he, ok := err.(*HTTPError); ok {
 | 
			
		||||
		code = he.Code
 | 
			
		||||
		msg = he.Message
 | 
			
		||||
		if he.Inner != nil {
 | 
			
		||||
			msg = fmt.Sprintf("%v, %v", err, he.Inner)
 | 
			
		||||
		}
 | 
			
		||||
	} else if e.Debug {
 | 
			
		||||
		msg = err.Error()
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -328,19 +332,19 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
 | 
			
		||||
		msg = Map{"message": msg}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	e.Logger.Error(err)
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	if !c.Response().Committed {
 | 
			
		||||
		if c.Request().Method == HEAD { // Issue #608
 | 
			
		||||
			if err := c.NoContent(code); err != nil {
 | 
			
		||||
				goto ERROR
 | 
			
		||||
			}
 | 
			
		||||
			err = c.NoContent(code)
 | 
			
		||||
		} else {
 | 
			
		||||
			if err := c.JSON(code, msg); err != nil {
 | 
			
		||||
				goto ERROR
 | 
			
		||||
			}
 | 
			
		||||
			err = c.JSON(code, msg)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			e.Logger.Error(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
ERROR:
 | 
			
		||||
	e.Logger.Error(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pre adds middleware to the chain which is run before router.
 | 
			
		||||
@@ -355,104 +359,114 @@ func (e *Echo) Use(middleware ...MiddlewareFunc) {
 | 
			
		||||
 | 
			
		||||
// CONNECT registers a new CONNECT route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(CONNECT, path, h, m...)
 | 
			
		||||
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(CONNECT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DELETE registers a new DELETE route for a path with matching handler in the router
 | 
			
		||||
// with optional route-level middleware.
 | 
			
		||||
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(DELETE, path, h, m...)
 | 
			
		||||
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(DELETE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GET registers a new GET route for a path with matching handler in the router
 | 
			
		||||
// with optional route-level middleware.
 | 
			
		||||
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(GET, path, h, m...)
 | 
			
		||||
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(GET, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HEAD registers a new HEAD route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(HEAD, path, h, m...)
 | 
			
		||||
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(HEAD, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OPTIONS registers a new OPTIONS route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(OPTIONS, path, h, m...)
 | 
			
		||||
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(OPTIONS, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PATCH registers a new PATCH route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(PATCH, path, h, m...)
 | 
			
		||||
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(PATCH, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// POST registers a new POST route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(POST, path, h, m...)
 | 
			
		||||
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(POST, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PUT registers a new PUT route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(PUT, path, h, m...)
 | 
			
		||||
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(PUT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TRACE registers a new TRACE route for a path with matching handler in the
 | 
			
		||||
// router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	e.add(TRACE, path, h, m...)
 | 
			
		||||
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return e.Add(TRACE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Any registers a new route for all HTTP methods and path with matching handler
 | 
			
		||||
// in the router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
 | 
			
		||||
	routes := make([]*Route, 0)
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		e.add(m, path, handler, middleware...)
 | 
			
		||||
		routes = append(routes, e.Add(m, path, handler, middleware...))
 | 
			
		||||
	}
 | 
			
		||||
	return routes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match registers a new route for multiple HTTP methods and path with matching
 | 
			
		||||
// handler in the router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
 | 
			
		||||
	routes := make([]*Route, 0)
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		e.add(m, path, handler, middleware...)
 | 
			
		||||
		routes = append(routes, e.Add(m, path, handler, middleware...))
 | 
			
		||||
	}
 | 
			
		||||
	return routes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Static registers a new route with path prefix to serve static files from the
 | 
			
		||||
// provided root directory.
 | 
			
		||||
func (e *Echo) Static(prefix, root string) {
 | 
			
		||||
func (e *Echo) Static(prefix, root string) *Route {
 | 
			
		||||
	if root == "" {
 | 
			
		||||
		root = "." // For security we want to restrict to CWD.
 | 
			
		||||
	}
 | 
			
		||||
	static(e, prefix, root)
 | 
			
		||||
	return static(e, prefix, root)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func static(i i, prefix, root string) {
 | 
			
		||||
func static(i i, prefix, root string) *Route {
 | 
			
		||||
	h := func(c Context) error {
 | 
			
		||||
		name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security
 | 
			
		||||
		p, err := PathUnescape(c.Param("*"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
 | 
			
		||||
		return c.File(name)
 | 
			
		||||
	}
 | 
			
		||||
	i.GET(prefix, h)
 | 
			
		||||
	if prefix == "/" {
 | 
			
		||||
		i.GET(prefix+"*", h)
 | 
			
		||||
	} else {
 | 
			
		||||
		i.GET(prefix+"/*", h)
 | 
			
		||||
		return i.GET(prefix+"*", h)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i.GET(prefix+"/*", h)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// File registers a new route with path to serve a static file.
 | 
			
		||||
func (e *Echo) File(path, file string) {
 | 
			
		||||
	e.GET(path, func(c Context) error {
 | 
			
		||||
func (e *Echo) File(path, file string) *Route {
 | 
			
		||||
	return e.GET(path, func(c Context) error {
 | 
			
		||||
		return c.File(file)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
// Add registers a new route for an HTTP method and path with matching handler
 | 
			
		||||
// in the router with optional route-level middleware.
 | 
			
		||||
func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	e.router.Add(method, path, func(c Context) error {
 | 
			
		||||
		h := handler
 | 
			
		||||
@@ -463,11 +477,12 @@ func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...Middl
 | 
			
		||||
		return h(c)
 | 
			
		||||
	})
 | 
			
		||||
	r := &Route{
 | 
			
		||||
		Method:  method,
 | 
			
		||||
		Path:    path,
 | 
			
		||||
		Handler: name,
 | 
			
		||||
		Method: method,
 | 
			
		||||
		Path:   path,
 | 
			
		||||
		Name:   name,
 | 
			
		||||
	}
 | 
			
		||||
	e.router.routes[method+path] = r
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Group creates a new router group with prefix and optional group-level middleware.
 | 
			
		||||
@@ -479,12 +494,22 @@ func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {
 | 
			
		||||
 | 
			
		||||
// URI generates a URI from handler.
 | 
			
		||||
func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	return e.Reverse(name, params...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// URL is an alias for `URI` function.
 | 
			
		||||
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	return e.URI(h, params...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reverse generates an URL from route name and provided parameters.
 | 
			
		||||
func (e *Echo) Reverse(name string, params ...interface{}) string {
 | 
			
		||||
	uri := new(bytes.Buffer)
 | 
			
		||||
	ln := len(params)
 | 
			
		||||
	n := 0
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	for _, r := range e.router.routes {
 | 
			
		||||
		if r.Handler == name {
 | 
			
		||||
		if r.Name == name {
 | 
			
		||||
			for i, l := 0, len(r.Path); i < l; i++ {
 | 
			
		||||
				if r.Path[i] == ':' && n < ln {
 | 
			
		||||
					for ; i < l && r.Path[i] != '/'; i++ {
 | 
			
		||||
@@ -502,11 +527,6 @@ func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	return uri.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// URL is an alias for `URI` function.
 | 
			
		||||
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
 | 
			
		||||
	return e.URI(h, params...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Routes returns the registered routes.
 | 
			
		||||
func (e *Echo) Routes() []*Route {
 | 
			
		||||
	routes := []*Route{}
 | 
			
		||||
@@ -653,7 +673,7 @@ func NewHTTPError(code int, message ...interface{}) *HTTPError {
 | 
			
		||||
 | 
			
		||||
// Error makes it compatible with `error` interface.
 | 
			
		||||
func (he *HTTPError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("code=%d, message=%s", he.Code, he.Message)
 | 
			
		||||
	return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -21,66 +21,66 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
 | 
			
		||||
	// Allow all requests to reach the group as they might get dropped if router
 | 
			
		||||
	// doesn't find a match, making none of the group middleware process.
 | 
			
		||||
	g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
		return NotFoundHandler(c)
 | 
			
		||||
	}, g.middleware...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(CONNECT, path, h, m...)
 | 
			
		||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(CONNECT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DELETE implements `Echo#DELETE()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(DELETE, path, h, m...)
 | 
			
		||||
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(DELETE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GET implements `Echo#GET()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(GET, path, h, m...)
 | 
			
		||||
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(GET, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HEAD implements `Echo#HEAD()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(HEAD, path, h, m...)
 | 
			
		||||
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(HEAD, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(OPTIONS, path, h, m...)
 | 
			
		||||
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(OPTIONS, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PATCH implements `Echo#PATCH()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(PATCH, path, h, m...)
 | 
			
		||||
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(PATCH, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// POST implements `Echo#POST()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(POST, path, h, m...)
 | 
			
		||||
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(POST, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PUT implements `Echo#PUT()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(PUT, path, h, m...)
 | 
			
		||||
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(PUT, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TRACE implements `Echo#TRACE()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	g.add(TRACE, path, h, m...)
 | 
			
		||||
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(TRACE, path, h, m...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Any implements `Echo#Any()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		g.add(m, path, handler, middleware...)
 | 
			
		||||
		g.Add(m, path, handler, middleware...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match implements `Echo#Match()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		g.add(m, path, handler, middleware...)
 | 
			
		||||
		g.Add(m, path, handler, middleware...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -102,12 +102,13 @@ func (g *Group) File(path, file string) {
 | 
			
		||||
	g.echo.File(g.prefix+path, file)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
// Add implements `Echo#Add()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
 | 
			
		||||
	// Combine into a new slice to avoid accidentally passing the same slice for
 | 
			
		||||
	// multiple routes, which would lead to later add() calls overwriting the
 | 
			
		||||
	// middleware from earlier calls.
 | 
			
		||||
	m := []MiddlewareFunc{}
 | 
			
		||||
	m = append(m, g.middleware...)
 | 
			
		||||
	m = append(m, middleware...)
 | 
			
		||||
	g.echo.add(method, g.prefix+path, handler, m...)
 | 
			
		||||
	return g.echo.Add(method, g.prefix+path, handler, m...)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,6 +3,7 @@ package middleware
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
@@ -27,7 +28,7 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	basic        = "Basic"
 | 
			
		||||
	basic        = "basic"
 | 
			
		||||
	defaultRealm = "Restricted"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +55,7 @@ func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
 | 
			
		||||
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Validator == nil {
 | 
			
		||||
		panic("basic-auth middleware requires a validator function")
 | 
			
		||||
		panic("echo: basic-auth middleware requires a validator function")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultBasicAuthConfig.Skipper
 | 
			
		||||
@@ -72,7 +73,7 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
			auth := c.Request().Header.Get(echo.HeaderAuthorization)
 | 
			
		||||
			l := len(basic)
 | 
			
		||||
 | 
			
		||||
			if len(auth) > l+1 && auth[:l] == basic {
 | 
			
		||||
			if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
 | 
			
		||||
				b, err := base64.StdEncoding.DecodeString(auth[l+1:])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// BodyDumpConfig defines the config for BodyDump middleware.
 | 
			
		||||
	BodyDumpConfig struct {
 | 
			
		||||
		// Skipper defines a function to skip middleware.
 | 
			
		||||
		Skipper Skipper
 | 
			
		||||
 | 
			
		||||
		// Handler receives request and response payload.
 | 
			
		||||
		// Required.
 | 
			
		||||
		Handler BodyDumpHandler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// BodyDumpHandler receives the request and response payload.
 | 
			
		||||
	BodyDumpHandler func(echo.Context, []byte, []byte)
 | 
			
		||||
 | 
			
		||||
	bodyDumpResponseWriter struct {
 | 
			
		||||
		io.Writer
 | 
			
		||||
		http.ResponseWriter
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultBodyDumpConfig is the default Gzip middleware config.
 | 
			
		||||
	DefaultBodyDumpConfig = BodyDumpConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// BodyDump returns a BodyDump middleware.
 | 
			
		||||
//
 | 
			
		||||
// BodyLimit middleware captures the request and response payload and calls the
 | 
			
		||||
// registered handler.
 | 
			
		||||
func BodyDump(handler BodyDumpHandler) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultBodyDumpConfig
 | 
			
		||||
	c.Handler = handler
 | 
			
		||||
	return BodyDumpWithConfig(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BodyDumpWithConfig returns a BodyDump middleware with config.
 | 
			
		||||
// See: `BodyDump()`.
 | 
			
		||||
func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Handler == nil {
 | 
			
		||||
		panic("echo: body-dump middleware requires a handler function")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultBodyDumpConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Request
 | 
			
		||||
			reqBody := []byte{}
 | 
			
		||||
			if c.Request().Body != nil { // Read
 | 
			
		||||
				reqBody, _ = ioutil.ReadAll(c.Request().Body)
 | 
			
		||||
			}
 | 
			
		||||
			c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset
 | 
			
		||||
 | 
			
		||||
			// Response
 | 
			
		||||
			resBody := new(bytes.Buffer)
 | 
			
		||||
			mw := io.MultiWriter(c.Response().Writer, resBody)
 | 
			
		||||
			writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer}
 | 
			
		||||
			c.Response().Writer = writer
 | 
			
		||||
 | 
			
		||||
			if err = next(c); err != nil {
 | 
			
		||||
				c.Error(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Callback
 | 
			
		||||
			config.Handler(c, reqBody, resBody.Bytes())
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) WriteHeader(code int) {
 | 
			
		||||
	w.ResponseWriter.WriteHeader(code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
 | 
			
		||||
	return w.Writer.Write(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) Flush() {
 | 
			
		||||
	w.ResponseWriter.(http.Flusher).Flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
 | 
			
		||||
	return w.ResponseWriter.(http.Hijacker).Hijack()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *bodyDumpResponseWriter) CloseNotify() <-chan bool {
 | 
			
		||||
	return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -30,7 +30,7 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultBodyLimitConfig is the default Gzip middleware config.
 | 
			
		||||
	// DefaultBodyLimitConfig is the default BodyLimit middleware config.
 | 
			
		||||
	DefaultBodyLimitConfig = BodyLimitConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
@@ -60,7 +60,7 @@ func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
	limit, err := bytes.Parse(config.Limit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(fmt.Errorf("invalid body-limit=%s", config.Limit))
 | 
			
		||||
		panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit))
 | 
			
		||||
	}
 | 
			
		||||
	config.limit = limit
 | 
			
		||||
	pool := limitedReaderPool(config)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -67,7 +67,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
 | 
			
		||||
			res := c.Response()
 | 
			
		||||
			res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
 | 
			
		||||
			if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
 | 
			
		||||
				res.Header().Add(echo.HeaderContentEncoding, gzipScheme) // Issue #806
 | 
			
		||||
				res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
 | 
			
		||||
				rw := res.Writer
 | 
			
		||||
				w, err := gzip.NewWriterLevel(rw, config.Level)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -98,6 +98,7 @@ func (w *gzipResponseWriter) WriteHeader(code int) {
 | 
			
		||||
	if code == http.StatusNoContent { // Issue #489
 | 
			
		||||
		w.ResponseWriter.Header().Del(echo.HeaderContentEncoding)
 | 
			
		||||
	}
 | 
			
		||||
	w.Header().Del(echo.HeaderContentLength) // Issue #444
 | 
			
		||||
	w.ResponseWriter.WriteHeader(code)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,7 +1,6 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -57,6 +56,12 @@ const (
 | 
			
		||||
	AlgorithmHS256 = "HS256"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Errors
 | 
			
		||||
var (
 | 
			
		||||
	ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "Missing or malformed jwt")
 | 
			
		||||
	ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired jwt")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultJWTConfig is the default JWT auth middleware config.
 | 
			
		||||
	DefaultJWTConfig = JWTConfig{
 | 
			
		||||
@@ -77,7 +82,7 @@ var (
 | 
			
		||||
//
 | 
			
		||||
// See: https://jwt.io/introduction
 | 
			
		||||
// See `JWTConfig.TokenLookup`
 | 
			
		||||
func JWT(key []byte) echo.MiddlewareFunc {
 | 
			
		||||
func JWT(key interface{}) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultJWTConfig
 | 
			
		||||
	c.SigningKey = key
 | 
			
		||||
	return JWTWithConfig(c)
 | 
			
		||||
@@ -134,14 +139,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
			auth, err := extractor(c)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return echo.NewHTTPError(http.StatusBadRequest, err.Error())
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			token := new(jwt.Token)
 | 
			
		||||
			// Issue #647, #656
 | 
			
		||||
			if _, ok := config.Claims.(jwt.MapClaims); ok {
 | 
			
		||||
				token, err = jwt.Parse(auth, config.keyFunc)
 | 
			
		||||
			} else {
 | 
			
		||||
				claims := reflect.ValueOf(config.Claims).Interface().(jwt.Claims)
 | 
			
		||||
				t := reflect.ValueOf(config.Claims).Type().Elem()
 | 
			
		||||
				claims := reflect.New(t).Interface().(jwt.Claims)
 | 
			
		||||
				token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
 | 
			
		||||
			}
 | 
			
		||||
			if err == nil && token.Valid {
 | 
			
		||||
@@ -149,7 +155,11 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 | 
			
		||||
				c.Set(config.ContextKey, token)
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
			return echo.ErrUnauthorized
 | 
			
		||||
			return &echo.HTTPError{
 | 
			
		||||
				Code:    ErrJWTInvalid.Code,
 | 
			
		||||
				Message: ErrJWTInvalid.Message,
 | 
			
		||||
				Inner:   err,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -162,7 +172,7 @@ func jwtFromHeader(header string, authScheme string) jwtExtractor {
 | 
			
		||||
		if len(auth) > l+1 && auth[:l] == authScheme {
 | 
			
		||||
			return auth[l+1:], nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", errors.New("Missing or invalid jwt in the request header")
 | 
			
		||||
		return "", ErrJWTMissing
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -171,7 +181,7 @@ func jwtFromQuery(param string) jwtExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		token := c.QueryParam(param)
 | 
			
		||||
		if token == "" {
 | 
			
		||||
			return "", errors.New("Missing jwt in the query string")
 | 
			
		||||
			return "", ErrJWTMissing
 | 
			
		||||
		}
 | 
			
		||||
		return token, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -182,7 +192,7 @@ func jwtFromCookie(name string) jwtExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		cookie, err := c.Cookie(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", errors.New("Missing jwt in the cookie")
 | 
			
		||||
			return "", ErrJWTMissing
 | 
			
		||||
		}
 | 
			
		||||
		return cookie.Value, nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -72,7 +72,7 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
		config.KeyLookup = DefaultKeyAuthConfig.KeyLookup
 | 
			
		||||
	}
 | 
			
		||||
	if config.Validator == nil {
 | 
			
		||||
		panic("key-auth middleware requires a validator function")
 | 
			
		||||
		panic("echo: key-auth middleware requires a validator function")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Initialize
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,7 +1,6 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
@@ -54,35 +53,38 @@ type (
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultProxyConfig is the default Proxy middleware config.
 | 
			
		||||
	DefaultProxyConfig = ProxyConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func proxyHTTP(t *ProxyTarget) http.Handler {
 | 
			
		||||
	return httputil.NewSingleHostReverseProxy(t.URL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		h, ok := w.(http.Hijacker)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			c.Error(errors.New("proxy raw, not a hijacker"))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		in, _, err := h.Hijack()
 | 
			
		||||
		in, _, err := c.Response().Hijack()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.URL, err))
 | 
			
		||||
			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		defer in.Close()
 | 
			
		||||
 | 
			
		||||
		out, err := net.Dial("tcp", t.URL.Host)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.URL, err))
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err))
 | 
			
		||||
			c.Error(he)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		defer out.Close()
 | 
			
		||||
 | 
			
		||||
		// Write header
 | 
			
		||||
		err = r.Write(out)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err))
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err))
 | 
			
		||||
			c.Error(he)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -97,7 +99,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
 | 
			
		||||
		go cp(in, out)
 | 
			
		||||
		err = <-errc
 | 
			
		||||
		if err != nil && err != io.EOF {
 | 
			
		||||
			c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err)
 | 
			
		||||
			c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@@ -118,8 +120,18 @@ func (r *RoundRobinBalancer) Next() *ProxyTarget {
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Proxy returns an HTTP/WebSocket reverse proxy middleware.
 | 
			
		||||
func Proxy(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
// Proxy returns a Proxy middleware.
 | 
			
		||||
//
 | 
			
		||||
// Proxy middleware forwards the request to upstream server using a configured load balancing technique.
 | 
			
		||||
func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultProxyConfig
 | 
			
		||||
	c.Balancer = balancer
 | 
			
		||||
	return ProxyWithConfig(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProxyWithConfig returns a Proxy middleware with config.
 | 
			
		||||
// See: `Proxy()`
 | 
			
		||||
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultLoggerConfig.Skipper
 | 
			
		||||
@@ -130,6 +142,10 @@ func Proxy(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			res := c.Response()
 | 
			
		||||
			tgt := config.Balancer.Next()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,7 +5,6 @@ import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
	"github.com/labstack/gommon/color"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
@@ -64,17 +63,14 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if r := recover(); r != nil {
 | 
			
		||||
					var err error
 | 
			
		||||
					switch r := r.(type) {
 | 
			
		||||
					case error:
 | 
			
		||||
						err = r
 | 
			
		||||
					default:
 | 
			
		||||
					err, ok := r.(error)
 | 
			
		||||
					if !ok {
 | 
			
		||||
						err = fmt.Errorf("%v", r)
 | 
			
		||||
					}
 | 
			
		||||
					stack := make([]byte, config.StackSize)
 | 
			
		||||
					length := runtime.Stack(stack, !config.DisableStackAll)
 | 
			
		||||
					if !config.DisablePrintStack {
 | 
			
		||||
						c.Logger().Printf("[%s] %s %s\n", color.Red("PANIC RECOVER"), err, stack[:length])
 | 
			
		||||
						c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length])
 | 
			
		||||
					}
 | 
			
		||||
					c.Error(err)
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -2,6 +2,7 @@ package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
@@ -66,7 +67,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
@@ -75,17 +76,25 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
			if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`.
 | 
			
		||||
				p = c.Param("*")
 | 
			
		||||
			}
 | 
			
		||||
			p, err = echo.PathUnescape(p)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
 | 
			
		||||
 | 
			
		||||
			fi, err := os.Stat(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if os.IsNotExist(err) {
 | 
			
		||||
					if config.HTML5 && path.Ext(p) == "" {
 | 
			
		||||
						return c.File(filepath.Join(config.Root, config.Index))
 | 
			
		||||
					if err = next(c); err != nil {
 | 
			
		||||
						if he, ok := err.(*echo.HTTPError); ok {
 | 
			
		||||
							if config.HTML5 && he.Code == http.StatusNotFound {
 | 
			
		||||
								return c.File(filepath.Join(config.Root, config.Index))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					return next(c)
 | 
			
		||||
				}
 | 
			
		||||
				return err
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if fi.IsDir() {
 | 
			
		||||
@@ -99,7 +108,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
					if os.IsNotExist(err) {
 | 
			
		||||
						return next(c)
 | 
			
		||||
					}
 | 
			
		||||
					return err
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return c.File(index)
 | 
			
		||||
@@ -110,20 +119,20 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listDir(name string, res *echo.Response) error {
 | 
			
		||||
func listDir(name string, res *echo.Response) (err error) {
 | 
			
		||||
	dir, err := os.Open(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dirs, err := dir.Readdir(-1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create a directory index
 | 
			
		||||
	res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
 | 
			
		||||
	if _, err = fmt.Fprintf(res, "<pre>\n"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range dirs {
 | 
			
		||||
		name := d.Name()
 | 
			
		||||
@@ -133,9 +142,9 @@ func listDir(name string, res *echo.Response) error {
 | 
			
		||||
			name += "/"
 | 
			
		||||
		}
 | 
			
		||||
		if _, err = fmt.Fprintf(res, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	_, err = fmt.Fprintf(res, "</pre>\n")
 | 
			
		||||
	return err
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,11 +11,12 @@ type (
 | 
			
		||||
	// by an HTTP handler to construct an HTTP response.
 | 
			
		||||
	// See: https://golang.org/pkg/net/http/#ResponseWriter
 | 
			
		||||
	Response struct {
 | 
			
		||||
		Writer    http.ResponseWriter
 | 
			
		||||
		Status    int
 | 
			
		||||
		Size      int64
 | 
			
		||||
		Committed bool
 | 
			
		||||
		echo      *Echo
 | 
			
		||||
		echo        *Echo
 | 
			
		||||
		beforeFuncs []func()
 | 
			
		||||
		Writer      http.ResponseWriter
 | 
			
		||||
		Status      int
 | 
			
		||||
		Size        int64
 | 
			
		||||
		Committed   bool
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +35,11 @@ func (r *Response) Header() http.Header {
 | 
			
		||||
	return r.Writer.Header()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before registers a function which is called just before the response is written.
 | 
			
		||||
func (r *Response) Before(fn func()) {
 | 
			
		||||
	r.beforeFuncs = append(r.beforeFuncs, fn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteHeader sends an HTTP response header with status code. If WriteHeader is
 | 
			
		||||
// not called explicitly, the first call to Write will trigger an implicit
 | 
			
		||||
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
 | 
			
		||||
@@ -43,6 +49,9 @@ func (r *Response) WriteHeader(code int) {
 | 
			
		||||
		r.echo.Logger.Warn("response already committed")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, fn := range r.beforeFuncs {
 | 
			
		||||
		fn()
 | 
			
		||||
	}
 | 
			
		||||
	r.Status = code
 | 
			
		||||
	r.Writer.WriteHeader(code)
 | 
			
		||||
	r.Committed = true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -394,7 +394,7 @@ func (r *Router) Find(method, path string, c Context) {
 | 
			
		||||
		if cn = cn.findChildByKind(akind); cn == nil {
 | 
			
		||||
			if nn != nil {
 | 
			
		||||
				cn = nn
 | 
			
		||||
				nn = nil // Next
 | 
			
		||||
				nn = cn.parent // Next (Issue #954)
 | 
			
		||||
				search = ns
 | 
			
		||||
				if nk == pkind {
 | 
			
		||||
					goto Param
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
// +build go1.7, !go1.8
 | 
			
		||||
 | 
			
		||||
package echo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PathUnescape is wraps `url.QueryUnescape`
 | 
			
		||||
func PathUnescape(s string) (string, error) {
 | 
			
		||||
	return url.QueryUnescape(s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
// +build go1.8
 | 
			
		||||
 | 
			
		||||
package echo
 | 
			
		||||
 | 
			
		||||
import "net/url"
 | 
			
		||||
 | 
			
		||||
// PathUnescape is wraps `url.PathUnescape`
 | 
			
		||||
func PathUnescape(s string) (string, error) {
 | 
			
		||||
	return url.PathUnescape(s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										80
									
								
								vendor/github.com/lrstanley/girc/builtin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/lrstanley/girc/builtin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,64 +16,62 @@ func (c *Client) registerBuiltins() {
 | 
			
		||||
	c.Handlers.mu.Lock()
 | 
			
		||||
 | 
			
		||||
	// Built-in things that should always be supported.
 | 
			
		||||
	c.Handlers.register(true, RPL_WELCOME, HandlerFunc(func(c *Client, e Event) {
 | 
			
		||||
		go handleConnect(c, e)
 | 
			
		||||
	}))
 | 
			
		||||
	c.Handlers.register(true, PING, HandlerFunc(handlePING))
 | 
			
		||||
	c.Handlers.register(true, PONG, HandlerFunc(handlePONG))
 | 
			
		||||
	c.Handlers.register(true, true, RPL_WELCOME, HandlerFunc(handleConnect))
 | 
			
		||||
	c.Handlers.register(true, false, PING, HandlerFunc(handlePING))
 | 
			
		||||
	c.Handlers.register(true, false, PONG, HandlerFunc(handlePONG))
 | 
			
		||||
 | 
			
		||||
	if !c.Config.disableTracking {
 | 
			
		||||
		// Joins/parts/anything that may add/remove/rename users.
 | 
			
		||||
		c.Handlers.register(true, JOIN, HandlerFunc(handleJOIN))
 | 
			
		||||
		c.Handlers.register(true, PART, HandlerFunc(handlePART))
 | 
			
		||||
		c.Handlers.register(true, KICK, HandlerFunc(handleKICK))
 | 
			
		||||
		c.Handlers.register(true, QUIT, HandlerFunc(handleQUIT))
 | 
			
		||||
		c.Handlers.register(true, NICK, HandlerFunc(handleNICK))
 | 
			
		||||
		c.Handlers.register(true, RPL_NAMREPLY, HandlerFunc(handleNAMES))
 | 
			
		||||
		c.Handlers.register(true, false, JOIN, HandlerFunc(handleJOIN))
 | 
			
		||||
		c.Handlers.register(true, false, PART, HandlerFunc(handlePART))
 | 
			
		||||
		c.Handlers.register(true, false, KICK, HandlerFunc(handleKICK))
 | 
			
		||||
		c.Handlers.register(true, false, QUIT, HandlerFunc(handleQUIT))
 | 
			
		||||
		c.Handlers.register(true, false, NICK, HandlerFunc(handleNICK))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_NAMREPLY, HandlerFunc(handleNAMES))
 | 
			
		||||
 | 
			
		||||
		// Modes.
 | 
			
		||||
		c.Handlers.register(true, MODE, HandlerFunc(handleMODE))
 | 
			
		||||
		c.Handlers.register(true, RPL_CHANNELMODEIS, HandlerFunc(handleMODE))
 | 
			
		||||
		c.Handlers.register(true, false, MODE, HandlerFunc(handleMODE))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_CHANNELMODEIS, HandlerFunc(handleMODE))
 | 
			
		||||
 | 
			
		||||
		// WHO/WHOX responses.
 | 
			
		||||
		c.Handlers.register(true, RPL_WHOREPLY, HandlerFunc(handleWHO))
 | 
			
		||||
		c.Handlers.register(true, RPL_WHOSPCRPL, HandlerFunc(handleWHO))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_WHOREPLY, HandlerFunc(handleWHO))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_WHOSPCRPL, HandlerFunc(handleWHO))
 | 
			
		||||
 | 
			
		||||
		// Other misc. useful stuff.
 | 
			
		||||
		c.Handlers.register(true, TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, RPL_TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, RPL_MYINFO, HandlerFunc(handleMYINFO))
 | 
			
		||||
		c.Handlers.register(true, RPL_ISUPPORT, HandlerFunc(handleISUPPORT))
 | 
			
		||||
		c.Handlers.register(true, RPL_MOTDSTART, HandlerFunc(handleMOTD))
 | 
			
		||||
		c.Handlers.register(true, RPL_MOTD, HandlerFunc(handleMOTD))
 | 
			
		||||
		c.Handlers.register(true, false, TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_MYINFO, HandlerFunc(handleMYINFO))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_ISUPPORT, HandlerFunc(handleISUPPORT))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_MOTDSTART, HandlerFunc(handleMOTD))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_MOTD, HandlerFunc(handleMOTD))
 | 
			
		||||
 | 
			
		||||
		// Keep users lastactive times up to date.
 | 
			
		||||
		c.Handlers.register(true, PRIVMSG, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, NOTICE, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, TOPIC, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, KICK, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, PRIVMSG, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, NOTICE, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, TOPIC, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, KICK, HandlerFunc(updateLastActive))
 | 
			
		||||
 | 
			
		||||
		// CAP IRCv3-specific tracking and functionality.
 | 
			
		||||
		c.Handlers.register(true, CAP, HandlerFunc(handleCAP))
 | 
			
		||||
		c.Handlers.register(true, CAP_CHGHOST, HandlerFunc(handleCHGHOST))
 | 
			
		||||
		c.Handlers.register(true, CAP_AWAY, HandlerFunc(handleAWAY))
 | 
			
		||||
		c.Handlers.register(true, CAP_ACCOUNT, HandlerFunc(handleACCOUNT))
 | 
			
		||||
		c.Handlers.register(true, ALL_EVENTS, HandlerFunc(handleTags))
 | 
			
		||||
		c.Handlers.register(true, false, CAP, HandlerFunc(handleCAP))
 | 
			
		||||
		c.Handlers.register(true, false, CAP_CHGHOST, HandlerFunc(handleCHGHOST))
 | 
			
		||||
		c.Handlers.register(true, false, CAP_AWAY, HandlerFunc(handleAWAY))
 | 
			
		||||
		c.Handlers.register(true, false, CAP_ACCOUNT, HandlerFunc(handleACCOUNT))
 | 
			
		||||
		c.Handlers.register(true, false, ALL_EVENTS, HandlerFunc(handleTags))
 | 
			
		||||
 | 
			
		||||
		// SASL IRCv3 support.
 | 
			
		||||
		c.Handlers.register(true, AUTHENTICATE, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, RPL_SASLSUCCESS, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, RPL_NICKLOCKED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, ERR_SASLFAIL, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, ERR_SASLTOOLONG, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, ERR_SASLABORTED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, RPL_SASLMECHS, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, AUTHENTICATE, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_SASLSUCCESS, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_NICKLOCKED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, ERR_SASLFAIL, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, ERR_SASLTOOLONG, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, ERR_SASLABORTED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_SASLMECHS, HandlerFunc(handleSASLError))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Nickname collisions.
 | 
			
		||||
	c.Handlers.register(true, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, false, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, false, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, false, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
 | 
			
		||||
	c.Handlers.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
@@ -389,7 +387,7 @@ func handleISUPPORT(c *Client, e Event) {
 | 
			
		||||
	c.state.Lock()
 | 
			
		||||
	// Skip the first parameter, as it's our nickname.
 | 
			
		||||
	for i := 1; i < len(e.Params); i++ {
 | 
			
		||||
		j := strings.IndexByte(e.Params[i], 0x3D) // =
 | 
			
		||||
		j := strings.IndexByte(e.Params[i], '=')
 | 
			
		||||
 | 
			
		||||
		if j < 1 || (j+1) == len(e.Params[i]) {
 | 
			
		||||
			c.state.serverOptions[e.Params[i]] = ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								vendor/github.com/lrstanley/girc/cap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/lrstanley/girc/cap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -136,7 +136,7 @@ func handleCAP(c *Client, e Event) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Let them know which ones we'd like to enable.
 | 
			
		||||
			c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")})
 | 
			
		||||
			c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " "), EmptyTrailing: true})
 | 
			
		||||
 | 
			
		||||
			// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests
 | 
			
		||||
			// due to cap-notify, we can re-evaluate what we can support.
 | 
			
		||||
@@ -375,11 +375,11 @@ func handleTags(c *Client, e Event) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	prefixTag      byte = 0x40 // @
 | 
			
		||||
	prefixTagValue byte = 0x3D // =
 | 
			
		||||
	prefixUserTag  byte = 0x2B // +
 | 
			
		||||
	tagSeparator   byte = 0x3B // ;
 | 
			
		||||
	maxTagLength   int  = 511  // 510 + @ and " " (space), though space usually not included.
 | 
			
		||||
	prefixTag      byte = '@'
 | 
			
		||||
	prefixTagValue byte = '='
 | 
			
		||||
	prefixUserTag  byte = '+'
 | 
			
		||||
	tagSeparator   byte = ';'
 | 
			
		||||
	maxTagLength   int  = 511 // 510 + @ and " " (space), though space usually not included.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Tags represents the key-value pairs in IRCv3 message tags. The map contains
 | 
			
		||||
@@ -618,7 +618,7 @@ func validTag(name string) bool {
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(name); i++ {
 | 
			
		||||
		// A-Z, a-z, 0-9, -/._
 | 
			
		||||
		if (name[i] < 0x41 || name[i] > 0x5A) && (name[i] < 0x61 || name[i] > 0x7A) && (name[i] < 0x2D || name[i] > 0x39) && name[i] != 0x5F {
 | 
			
		||||
		if (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '-' || name[i] > '9') && name[i] != '_' {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -631,7 +631,7 @@ func validTag(name string) bool {
 | 
			
		||||
func validTagValue(value string) bool {
 | 
			
		||||
	for i := 0; i < len(value); i++ {
 | 
			
		||||
		// Don't allow any invisible chars within the tag, or semicolons.
 | 
			
		||||
		if value[i] < 0x21 || value[i] > 0x7E || value[i] == 0x3B {
 | 
			
		||||
		if value[i] < '!' || value[i] > '~' || value[i] == ';' {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								vendor/github.com/lrstanley/girc/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/lrstanley/girc/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -191,18 +191,6 @@ func (conf *Config) isValid() error {
 | 
			
		||||
// connected.
 | 
			
		||||
var ErrNotConnected = errors.New("client is not connected to server")
 | 
			
		||||
 | 
			
		||||
// ErrDisconnected is called when Config.Retries is less than 1, and we
 | 
			
		||||
// non-intentionally disconnected from the server.
 | 
			
		||||
var ErrDisconnected = errors.New("unexpectedly disconnected")
 | 
			
		||||
 | 
			
		||||
// ErrInvalidTarget should be returned if the target which you are
 | 
			
		||||
// attempting to send an event to is invalid or doesn't match RFC spec.
 | 
			
		||||
type ErrInvalidTarget struct {
 | 
			
		||||
	Target string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *ErrInvalidTarget) Error() string { return "invalid target: " + e.Target }
 | 
			
		||||
 | 
			
		||||
// New creates a new IRC client with the specified server, name and config.
 | 
			
		||||
func New(config Config) *Client {
 | 
			
		||||
	c := &Client{
 | 
			
		||||
@@ -253,6 +241,37 @@ func (c *Client) String() string {
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TLSConnectionState returns the TLS connection state from tls.Conn{}, which
 | 
			
		||||
// is useful to return needed TLS fingerprint info, certificates, verify cert
 | 
			
		||||
// expiration dates, etc. Will only return an error if the underlying
 | 
			
		||||
// connection wasn't established using TLS (see ErrConnNotTLS), or if the
 | 
			
		||||
// client isn't connected.
 | 
			
		||||
func (c *Client) TLSConnectionState() (*tls.ConnectionState, error) {
 | 
			
		||||
	c.mu.RLock()
 | 
			
		||||
	defer c.mu.RUnlock()
 | 
			
		||||
	if c.conn == nil {
 | 
			
		||||
		return nil, ErrNotConnected
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.conn.mu.RLock()
 | 
			
		||||
	defer c.conn.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if !c.conn.connected {
 | 
			
		||||
		return nil, ErrNotConnected
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tlsConn, ok := c.conn.sock.(*tls.Conn); ok {
 | 
			
		||||
		cs := tlsConn.ConnectionState()
 | 
			
		||||
		return &cs, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, ErrConnNotTLS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrConnNotTLS is returned when Client.TLSConnectionState() is called, and
 | 
			
		||||
// the connection to the server wasn't made with TLS.
 | 
			
		||||
var ErrConnNotTLS = errors.New("underlying connection is not tls")
 | 
			
		||||
 | 
			
		||||
// Close closes the network connection to the server, and sends a STOPPED
 | 
			
		||||
// event. This should cause Connect() to return with nil. This should be
 | 
			
		||||
// safe to call multiple times. See Connect()'s documentation on how
 | 
			
		||||
@@ -387,7 +406,7 @@ func (c *Client) ConnSince() (since *time.Duration, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsConnected returns true if the client is connected to the server.
 | 
			
		||||
func (c *Client) IsConnected() (connected bool) {
 | 
			
		||||
func (c *Client) IsConnected() bool {
 | 
			
		||||
	c.mu.RLock()
 | 
			
		||||
	if c.conn == nil {
 | 
			
		||||
		c.mu.RUnlock()
 | 
			
		||||
@@ -395,7 +414,7 @@ func (c *Client) IsConnected() (connected bool) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.conn.mu.RLock()
 | 
			
		||||
	connected = c.conn.connected
 | 
			
		||||
	connected := c.conn.connected
 | 
			
		||||
	c.conn.mu.RUnlock()
 | 
			
		||||
	c.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
@@ -445,9 +464,9 @@ func (c *Client) GetHost() string {
 | 
			
		||||
	return c.state.host
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Channels returns the active list of channels that the client is in.
 | 
			
		||||
// ChannelList returns the active list of channel names that the client is in.
 | 
			
		||||
// Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) Channels() []string {
 | 
			
		||||
func (c *Client) ChannelList() []string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
@@ -463,9 +482,26 @@ func (c *Client) Channels() []string {
 | 
			
		||||
	return channels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Users returns the active list of users that the client is tracking across
 | 
			
		||||
// all files. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) Users() []string {
 | 
			
		||||
// Channels returns the active channels that the client is in. Panics if
 | 
			
		||||
// tracking is disabled.
 | 
			
		||||
func (c *Client) Channels() []*Channel {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
	channels := make([]*Channel, len(c.state.channels))
 | 
			
		||||
	var i int
 | 
			
		||||
	for channel := range c.state.channels {
 | 
			
		||||
		channels[i] = c.state.channels[channel].Copy()
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	c.state.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return channels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserList returns the active list of nicknames that the client is tracking
 | 
			
		||||
// across all networks. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) UserList() []string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
@@ -481,6 +517,23 @@ func (c *Client) Users() []string {
 | 
			
		||||
	return users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Users returns the active users that the client is tracking across all
 | 
			
		||||
// networks. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) Users() []*User {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
	users := make([]*User, len(c.state.users))
 | 
			
		||||
	var i int
 | 
			
		||||
	for user := range c.state.users {
 | 
			
		||||
		users[i] = c.state.users[user].Copy()
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	c.state.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LookupChannel looks up a given channel in state. If the channel doesn't
 | 
			
		||||
// exist, nil is returned. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) LookupChannel(name string) *Channel {
 | 
			
		||||
@@ -562,30 +615,30 @@ func (c *Client) NetworkName() (name string) {
 | 
			
		||||
// supplied this information during connection. May be empty if the server
 | 
			
		||||
// does not support RPL_MYINFO. Will panic if used when tracking has been
 | 
			
		||||
// disabled.
 | 
			
		||||
func (c *Client) ServerVersion() (version string) {
 | 
			
		||||
func (c *Client) ServerVersion() string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	version, _ = c.GetServerOption("VERSION")
 | 
			
		||||
	version, _ := c.GetServerOption("VERSION")
 | 
			
		||||
 | 
			
		||||
	return version
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerMOTD returns the servers message of the day, if the server has sent
 | 
			
		||||
// it upon connect. Will panic if used when tracking has been disabled.
 | 
			
		||||
func (c *Client) ServerMOTD() (motd string) {
 | 
			
		||||
func (c *Client) ServerMOTD() string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
	motd = c.state.motd
 | 
			
		||||
	motd := c.state.motd
 | 
			
		||||
	c.state.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return motd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Lag is the latency between the server and the client. This is measured by
 | 
			
		||||
// determining the difference in time between when we ping the server, and
 | 
			
		||||
// Latency is the latency between the server and the client. This is measured
 | 
			
		||||
// by determining the difference in time between when we ping the server, and
 | 
			
		||||
// when we receive a pong.
 | 
			
		||||
func (c *Client) Lag() time.Duration {
 | 
			
		||||
func (c *Client) Latency() time.Duration {
 | 
			
		||||
	c.mu.RLock()
 | 
			
		||||
	c.conn.mu.RLock()
 | 
			
		||||
	delta := c.conn.lastPong.Sub(c.conn.lastPing)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,8 +12,9 @@ import (
 | 
			
		||||
 | 
			
		||||
// Input is a wrapper for events, based around private messages.
 | 
			
		||||
type Input struct {
 | 
			
		||||
	Origin *girc.Event
 | 
			
		||||
	Args   []string
 | 
			
		||||
	Origin  *girc.Event
 | 
			
		||||
	Args    []string
 | 
			
		||||
	RawArgs string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Command is an IRC command, supporting aliases, help documentation and easy
 | 
			
		||||
@@ -189,8 +190,9 @@ func (ch *CmdHandler) Execute(client *girc.Client, event girc.Event) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	in := &Input{
 | 
			
		||||
		Origin: &event,
 | 
			
		||||
		Args:   args,
 | 
			
		||||
		Origin:  &event,
 | 
			
		||||
		Args:    args,
 | 
			
		||||
		RawArgs: parsed[2],
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go cmd.Fn(client, in)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										261
									
								
								vendor/github.com/lrstanley/girc/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										261
									
								
								vendor/github.com/lrstanley/girc/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,18 +16,13 @@ type Commands struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Nick changes the client nickname.
 | 
			
		||||
func (cmd *Commands) Nick(name string) error {
 | 
			
		||||
	if !IsValidNick(name) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: name}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) Nick(name string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: NICK, Params: []string{name}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Join attempts to enter a list of IRC channels, at bulk if possible to
 | 
			
		||||
// prevent sending extensive JOIN commands.
 | 
			
		||||
func (cmd *Commands) Join(channels ...string) error {
 | 
			
		||||
func (cmd *Commands) Join(channels ...string) {
 | 
			
		||||
	// We can join multiple channels at once, however we need to ensure that
 | 
			
		||||
	// we are not exceeding the line length. (see maxLength)
 | 
			
		||||
	max := maxLength - len(JOIN) - 1
 | 
			
		||||
@@ -35,10 +30,6 @@ func (cmd *Commands) Join(channels ...string) error {
 | 
			
		||||
	var buffer string
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(channels); i++ {
 | 
			
		||||
		if !IsValidChannel(channels[i]) {
 | 
			
		||||
			return &ErrInvalidTarget{Target: channels[i]}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(buffer+","+channels[i]) > max {
 | 
			
		||||
			cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
 | 
			
		||||
			buffer = ""
 | 
			
		||||
@@ -53,91 +44,74 @@ func (cmd *Commands) Join(channels ...string) error {
 | 
			
		||||
 | 
			
		||||
		if i == len(channels)-1 {
 | 
			
		||||
			cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
 | 
			
		||||
			return nil
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinKey attempts to enter an IRC channel with a password.
 | 
			
		||||
func (cmd *Commands) JoinKey(channel, password string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) JoinKey(channel, password string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Part leaves an IRC channel.
 | 
			
		||||
func (cmd *Commands) Part(channel, message string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
func (cmd *Commands) Part(channels ...string) {
 | 
			
		||||
	for i := 0; i < len(channels); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: PART, Params: []string{channels[i]}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PartMessage leaves an IRC channel with a specified leave message.
 | 
			
		||||
func (cmd *Commands) PartMessage(channel, message string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) PartMessage(channel, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: PART, Params: []string{channel}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCP sends a CTCP request to target. Note that this method uses
 | 
			
		||||
// PRIVMSG specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCP(target, ctcpType, message string) error {
 | 
			
		||||
// PRIVMSG specifically. ctcpType is the CTCP command, e.g. "FINGER", "TIME",
 | 
			
		||||
// "VERSION", etc.
 | 
			
		||||
func (cmd *Commands) SendCTCP(target, ctcpType, message string) {
 | 
			
		||||
	out := encodeCTCPRaw(ctcpType, message)
 | 
			
		||||
	if out == "" {
 | 
			
		||||
		return errors.New("invalid CTCP")
 | 
			
		||||
		panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Message(target, out)
 | 
			
		||||
	cmd.Message(target, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCPf sends a CTCP request to target using a specific format. Note that
 | 
			
		||||
// this method uses PRIVMSG specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
// this method uses PRIVMSG specifically. ctcpType is the CTCP command, e.g.
 | 
			
		||||
// "FINGER", "TIME", "VERSION", etc.
 | 
			
		||||
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) {
 | 
			
		||||
	cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCPReplyf sends a CTCP response to target using a specific format.
 | 
			
		||||
// Note that this method uses NOTICE specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
// Note that this method uses NOTICE specifically. ctcpType is the CTCP
 | 
			
		||||
// command, e.g. "FINGER", "TIME", "VERSION", etc.
 | 
			
		||||
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) {
 | 
			
		||||
	cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCPReply sends a CTCP response to target. Note that this method uses
 | 
			
		||||
// NOTICE specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) error {
 | 
			
		||||
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) {
 | 
			
		||||
	out := encodeCTCPRaw(ctcpType, message)
 | 
			
		||||
	if out == "" {
 | 
			
		||||
		return errors.New("invalid CTCP")
 | 
			
		||||
		panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Notice(target, out)
 | 
			
		||||
	cmd.Notice(target, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message sends a PRIVMSG to target (either channel, service, or user).
 | 
			
		||||
func (cmd *Commands) Message(target, message string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) Message(target, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messagef sends a formated PRIVMSG to target (either channel, service, or
 | 
			
		||||
// user).
 | 
			
		||||
func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Message(target, fmt.Sprintf(format, a...))
 | 
			
		||||
func (cmd *Commands) Messagef(target, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Message(target, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrInvalidSource is returned when a method needs to know the origin of an
 | 
			
		||||
@@ -146,94 +120,95 @@ func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
 | 
			
		||||
var ErrInvalidSource = errors.New("event has nil or invalid source address")
 | 
			
		||||
 | 
			
		||||
// Reply sends a reply to channel or user, based on where the supplied event
 | 
			
		||||
// originated from. See also ReplyTo().
 | 
			
		||||
func (cmd *Commands) Reply(event Event, message string) error {
 | 
			
		||||
// originated from. See also ReplyTo(). Panics if the incoming event has no
 | 
			
		||||
// source.
 | 
			
		||||
func (cmd *Commands) Reply(event Event, message string) {
 | 
			
		||||
	if event.Source == nil {
 | 
			
		||||
		return ErrInvalidSource
 | 
			
		||||
		panic(ErrInvalidSource)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
 | 
			
		||||
		return cmd.Message(event.Params[0], message)
 | 
			
		||||
		cmd.Message(event.Params[0], message)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Message(event.Source.Name, message)
 | 
			
		||||
	cmd.Message(event.Source.Name, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Replyf sends a reply to channel or user with a format string, based on
 | 
			
		||||
// where the supplied event originated from. See also ReplyTof().
 | 
			
		||||
func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Reply(event, fmt.Sprintf(format, a...))
 | 
			
		||||
// where the supplied event originated from. See also ReplyTof(). Panics if
 | 
			
		||||
// the incoming event has no source.
 | 
			
		||||
func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Reply(event, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReplyTo sends a reply to a channel or user, based on where the supplied
 | 
			
		||||
// event originated from. ReplyTo(), when originating from a channel will
 | 
			
		||||
// default to replying with "<user>, <message>". See also Reply().
 | 
			
		||||
func (cmd *Commands) ReplyTo(event Event, message string) error {
 | 
			
		||||
// default to replying with "<user>, <message>". See also Reply(). Panics if
 | 
			
		||||
// the incoming event has no source.
 | 
			
		||||
func (cmd *Commands) ReplyTo(event Event, message string) {
 | 
			
		||||
	if event.Source == nil {
 | 
			
		||||
		return ErrInvalidSource
 | 
			
		||||
		panic(ErrInvalidSource)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
 | 
			
		||||
		return cmd.Message(event.Params[0], event.Source.Name+", "+message)
 | 
			
		||||
		cmd.Message(event.Params[0], event.Source.Name+", "+message)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Message(event.Source.Name, message)
 | 
			
		||||
	cmd.Message(event.Source.Name, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReplyTof sends a reply to a channel or user with a format string, based
 | 
			
		||||
// on where the supplied event originated from. ReplyTo(), when originating
 | 
			
		||||
// from a channel will default to replying with "<user>, <message>". See
 | 
			
		||||
// also Replyf().
 | 
			
		||||
func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.ReplyTo(event, fmt.Sprintf(format, a...))
 | 
			
		||||
// also Replyf(). Panics if the incoming event has no source.
 | 
			
		||||
func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) {
 | 
			
		||||
	cmd.ReplyTo(event, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Action sends a PRIVMSG ACTION (/me) to target (either channel, service,
 | 
			
		||||
// or user).
 | 
			
		||||
func (cmd *Commands) Action(target, message string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) Action(target, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{
 | 
			
		||||
		Command:  PRIVMSG,
 | 
			
		||||
		Params:   []string{target},
 | 
			
		||||
		Trailing: fmt.Sprintf("\001ACTION %s\001", message),
 | 
			
		||||
	})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel,
 | 
			
		||||
// service, or user).
 | 
			
		||||
func (cmd *Commands) Actionf(target, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Action(target, fmt.Sprintf(format, a...))
 | 
			
		||||
func (cmd *Commands) Actionf(target, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Action(target, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Notice sends a NOTICE to target (either channel, service, or user).
 | 
			
		||||
func (cmd *Commands) Notice(target, message string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) Notice(target, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Noticef sends a formated NOTICE to target (either channel, service, or
 | 
			
		||||
// user).
 | 
			
		||||
func (cmd *Commands) Noticef(target, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Notice(target, fmt.Sprintf(format, a...))
 | 
			
		||||
func (cmd *Commands) Noticef(target, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Notice(target, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendRaw sends a raw string back to the server, without carriage returns
 | 
			
		||||
// or newlines.
 | 
			
		||||
func (cmd *Commands) SendRaw(raw string) error {
 | 
			
		||||
	e := ParseEvent(raw)
 | 
			
		||||
	if e == nil {
 | 
			
		||||
		return errors.New("invalid event: " + raw)
 | 
			
		||||
// SendRaw sends a raw string (or multiple) to the server, without carriage
 | 
			
		||||
// returns or newlines. Returns an error if one of the raw strings cannot be
 | 
			
		||||
// properly parsed.
 | 
			
		||||
func (cmd *Commands) SendRaw(raw ...string) error {
 | 
			
		||||
	var event *Event
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(raw); i++ {
 | 
			
		||||
		event = ParseEvent(raw[i])
 | 
			
		||||
		if event == nil {
 | 
			
		||||
			return errors.New("invalid event: " + raw[i])
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cmd.c.Send(event)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(e)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -246,31 +221,26 @@ func (cmd *Commands) SendRawf(format string, a ...interface{}) error {
 | 
			
		||||
// Topic sets the topic of channel to message. Does not verify the length
 | 
			
		||||
// of the topic.
 | 
			
		||||
func (cmd *Commands) Topic(channel, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message})
 | 
			
		||||
	cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Who sends a WHO query to the server, which will attempt WHOX by default.
 | 
			
		||||
// See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This
 | 
			
		||||
// sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with
 | 
			
		||||
// girc's builtin tracking functionality.
 | 
			
		||||
func (cmd *Commands) Who(target string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) && !IsValidUser(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
func (cmd *Commands) Who(users ...string) {
 | 
			
		||||
	for i := 0; i < len(users); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: WHO, Params: []string{users[i], "%tcuhnr,2"}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhnr,2"}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Whois sends a WHOIS query to the server, targeted at a specific user.
 | 
			
		||||
// as WHOIS is a bit slower, you may want to use WHO for brief user info.
 | 
			
		||||
func (cmd *Commands) Whois(nick string) error {
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
// Whois sends a WHOIS query to the server, targeted at a specific user (or
 | 
			
		||||
// set of users). As WHOIS is a bit slower, you may want to use WHO for brief
 | 
			
		||||
// user info.
 | 
			
		||||
func (cmd *Commands) Whois(users ...string) {
 | 
			
		||||
	for i := 0; i < len(users); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: WHOIS, Params: []string{users[i]}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHOIS, Params: []string{nick}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ping sends a PING query to the server, with a specific identifier that
 | 
			
		||||
@@ -294,36 +264,40 @@ func (cmd *Commands) Oper(user, pass string) {
 | 
			
		||||
// Kick sends a KICK query to the server, attempting to kick nick from
 | 
			
		||||
// channel, with reason. If reason is blank, one will not be sent to the
 | 
			
		||||
// server.
 | 
			
		||||
func (cmd *Commands) Kick(channel, nick, reason string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) Kick(channel, user, reason string) {
 | 
			
		||||
	if reason != "" {
 | 
			
		||||
		cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}, Trailing: reason})
 | 
			
		||||
		return nil
 | 
			
		||||
		cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}, Trailing: reason, EmptyTrailing: true})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}})
 | 
			
		||||
	return nil
 | 
			
		||||
	cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ban adds the +b mode on the given mask on a channel.
 | 
			
		||||
func (cmd *Commands) Ban(channel, mask string) {
 | 
			
		||||
	cmd.Mode(channel, "+b", mask)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unban removes the +b mode on the given mask on a channel.
 | 
			
		||||
func (cmd *Commands) Unban(channel, mask string) {
 | 
			
		||||
	cmd.Mode(channel, "-b", mask)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mode sends a mode change to the server which should be applied to target
 | 
			
		||||
// (usually a channel or user), along with a set of modes (generally "+m",
 | 
			
		||||
// "+mmmm", or "-m", where "m" is the mode you want to change). Params is only
 | 
			
		||||
// needed if the mode change requires a parameter (ban or invite-only exclude.)
 | 
			
		||||
func (cmd *Commands) Mode(target, modes string, params ...string) {
 | 
			
		||||
	out := []string{target, modes}
 | 
			
		||||
	out = append(out, params...)
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: MODE, Params: out})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Invite sends a INVITE query to the server, to invite nick to channel.
 | 
			
		||||
func (cmd *Commands) Invite(channel, nick string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
func (cmd *Commands) Invite(channel string, users ...string) {
 | 
			
		||||
	for i := 0; i < len(users); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: INVITE, Params: []string{users[i], channel}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: INVITE, Params: []string{nick, channel}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Away sends a AWAY query to the server, suggesting that the client is no
 | 
			
		||||
@@ -348,10 +322,10 @@ func (cmd *Commands) Back() {
 | 
			
		||||
// Supports multiple channels at once, in hopes it will reduce extensive
 | 
			
		||||
// LIST queries to the server. Supply no channels to run a list against the
 | 
			
		||||
// entire server (warning, that may mean LOTS of channels!)
 | 
			
		||||
func (cmd *Commands) List(channels ...string) error {
 | 
			
		||||
func (cmd *Commands) List(channels ...string) {
 | 
			
		||||
	if len(channels) == 0 {
 | 
			
		||||
		cmd.c.Send(&Event{Command: LIST})
 | 
			
		||||
		return nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We can LIST multiple channels at once, however we need to ensure that
 | 
			
		||||
@@ -361,10 +335,6 @@ func (cmd *Commands) List(channels ...string) error {
 | 
			
		||||
	var buffer string
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(channels); i++ {
 | 
			
		||||
		if !IsValidChannel(channels[i]) {
 | 
			
		||||
			return &ErrInvalidTarget{Target: channels[i]}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(buffer+","+channels[i]) > max {
 | 
			
		||||
			cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
 | 
			
		||||
			buffer = ""
 | 
			
		||||
@@ -379,20 +349,13 @@ func (cmd *Commands) List(channels ...string) error {
 | 
			
		||||
 | 
			
		||||
		if i == len(channels)-1 {
 | 
			
		||||
			cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
 | 
			
		||||
			return nil
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Whowas sends a WHOWAS query to the server. amount is the amount of results
 | 
			
		||||
// you want back.
 | 
			
		||||
func (cmd *Commands) Whowas(nick string, amount int) error {
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHOWAS, Params: []string{nick, string(amount)}})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) Whowas(user string, amount int) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHOWAS, Params: []string{user, string(amount)}})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/lrstanley/girc/ctcp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/lrstanley/girc/ctcp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -58,7 +58,7 @@ func decodeCTCP(e *Event) *CTCPEvent {
 | 
			
		||||
	if s < 0 {
 | 
			
		||||
		for i := 0; i < len(text); i++ {
 | 
			
		||||
			// Check for A-Z, 0-9.
 | 
			
		||||
			if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
 | 
			
		||||
			if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -74,7 +74,7 @@ func decodeCTCP(e *Event) *CTCPEvent {
 | 
			
		||||
	// Loop through checking the tag first.
 | 
			
		||||
	for i := 0; i < s; i++ {
 | 
			
		||||
		// Check for A-Z, 0-9.
 | 
			
		||||
		if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
 | 
			
		||||
		if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -168,7 +168,7 @@ func (c *CTCP) parseCMD(cmd string) string {
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(cmd); i++ {
 | 
			
		||||
		// Check for A-Z, 0-9.
 | 
			
		||||
		if (cmd[i] < 0x41 || cmd[i] > 0x5A) && (cmd[i] < 0x30 || cmd[i] > 0x39) {
 | 
			
		||||
		if (cmd[i] < 'A' || cmd[i] > 'Z') && (cmd[i] < '0' || cmd[i] > '9') {
 | 
			
		||||
			return ""
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/lrstanley/girc/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/lrstanley/girc/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,8 +11,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	eventSpace byte = 0x20 // Separator.
 | 
			
		||||
	maxLength       = 510  // Maximum length is 510 (2 for line endings).
 | 
			
		||||
	eventSpace byte = ' ' // Separator.
 | 
			
		||||
	maxLength       = 510 // Maximum length is 510 (2 for line endings).
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// cutCRFunc is used to trim CR characters from prefixes/messages.
 | 
			
		||||
@@ -256,7 +256,7 @@ func (e *Event) Bytes() []byte {
 | 
			
		||||
 | 
			
		||||
	// Strip newlines and carriage returns.
 | 
			
		||||
	for i := 0; i < len(out); i++ {
 | 
			
		||||
		if out[i] == 0x0A || out[i] == 0x0D {
 | 
			
		||||
		if out[i] == '\n' || out[i] == '\r' {
 | 
			
		||||
			out = append(out[:i], out[i+1:]...)
 | 
			
		||||
			i-- // Decrease the index so we can pick up where we left off.
 | 
			
		||||
		}
 | 
			
		||||
@@ -432,9 +432,9 @@ func (e *Event) StripAction() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	messagePrefix byte = 0x3A // ":" -- prefix or last argument
 | 
			
		||||
	prefixIdent   byte = 0x21 // "!" -- username
 | 
			
		||||
	prefixHost    byte = 0x40 // "@" -- hostname
 | 
			
		||||
	messagePrefix byte = ':' // Prefix or last argument.
 | 
			
		||||
	prefixIdent   byte = '!' // Username.
 | 
			
		||||
	prefixHost    byte = '@' // Hostname.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Source represents the sender of an IRC event, see RFC1459 section 2.3.1.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/lrstanley/girc/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/lrstanley/girc/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,8 +12,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	fmtOpenChar  = 0x7B // {
 | 
			
		||||
	fmtCloseChar = 0x7D // }
 | 
			
		||||
	fmtOpenChar  = '{'
 | 
			
		||||
	fmtCloseChar = '}'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var fmtColors = map[string]int{
 | 
			
		||||
@@ -113,7 +113,7 @@ func Fmt(text string) string {
 | 
			
		||||
 | 
			
		||||
		if last > -1 {
 | 
			
		||||
			// A-Z, a-z, and ","
 | 
			
		||||
			if text[i] != 0x2c && (text[i] <= 0x41 || text[i] >= 0x5a) && (text[i] <= 0x61 || text[i] >= 0x7a) {
 | 
			
		||||
			if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') {
 | 
			
		||||
				last = -1
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
@@ -127,10 +127,10 @@ func Fmt(text string) string {
 | 
			
		||||
// See Fmt() for more information.
 | 
			
		||||
func TrimFmt(text string) string {
 | 
			
		||||
	for color := range fmtColors {
 | 
			
		||||
		text = strings.Replace(text, "{"+color+"}", "", -1)
 | 
			
		||||
		text = strings.Replace(text, string(fmtOpenChar)+color+string(fmtCloseChar), "", -1)
 | 
			
		||||
	}
 | 
			
		||||
	for code := range fmtCodes {
 | 
			
		||||
		text = strings.Replace(text, "{"+code+"}", "", -1)
 | 
			
		||||
		text = strings.Replace(text, string(fmtOpenChar)+code+string(fmtCloseChar), "", -1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return text
 | 
			
		||||
@@ -175,9 +175,10 @@ func IsValidChannel(channel string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// #, +, !<channelid>, or &
 | 
			
		||||
	// Including "*" in the prefix list, as this is commonly used (e.g. ZNC)
 | 
			
		||||
	if bytes.IndexByte([]byte{0x21, 0x23, 0x26, 0x2A, 0x2B}, channel[0]) == -1 {
 | 
			
		||||
	// #, +, !<channelid>, ~, or &
 | 
			
		||||
	// Including "*" and "~" in the prefix list, as these are commonly used
 | 
			
		||||
	// (e.g. ZNC.)
 | 
			
		||||
	if bytes.IndexByte([]byte{'!', '#', '&', '*', '~', '+'}, channel[0]) == -1 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -186,14 +187,14 @@ func IsValidChannel(channel string) bool {
 | 
			
		||||
	//   1 (prefix) + 5 (id) + 1 (+, channel name)
 | 
			
		||||
	// On some networks, this may be extended with ISUPPORT capabilities,
 | 
			
		||||
	// however this is extremely uncommon.
 | 
			
		||||
	if channel[0] == 0x21 {
 | 
			
		||||
	if channel[0] == '!' {
 | 
			
		||||
		if len(channel) < 7 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check for valid ID
 | 
			
		||||
		for i := 1; i < 6; i++ {
 | 
			
		||||
			if (channel[i] < 0x30 || channel[i] > 0x39) && (channel[i] < 0x41 || channel[i] > 0x5A) {
 | 
			
		||||
			if (channel[i] < '0' || channel[i] > '9') && (channel[i] < 'A' || channel[i] > 'Z') {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -222,17 +223,15 @@ func IsValidNick(nick string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nick = ToRFC1459(nick)
 | 
			
		||||
 | 
			
		||||
	// Check the first index. Some characters aren't allowed for the first
 | 
			
		||||
	// index of an IRC nickname.
 | 
			
		||||
	if nick[0] < 0x41 || nick[0] > 0x7D {
 | 
			
		||||
		// a-z, A-Z, and _\[]{}^|
 | 
			
		||||
	if (nick[0] < 'A' || nick[0] > '}') && nick[0] != '?' {
 | 
			
		||||
		// a-z, A-Z, '_\[]{}^|', and '?' in the case of znc.
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 1; i < len(nick); i++ {
 | 
			
		||||
		if (nick[i] < 0x41 || nick[i] > 0x7D) && (nick[i] < 0x30 || nick[i] > 0x39) && nick[i] != 0x2D {
 | 
			
		||||
		if (nick[i] < 'A' || nick[i] > '}') && (nick[i] < '0' || nick[i] > '9') && nick[i] != '-' {
 | 
			
		||||
			// a-z, A-Z, 0-9, -, and _\[]{}^|
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
@@ -261,10 +260,8 @@ func IsValidUser(name string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name = ToRFC1459(name)
 | 
			
		||||
 | 
			
		||||
	// "~" is prepended (commonly) if there was no ident server response.
 | 
			
		||||
	if name[0] == 0x7E {
 | 
			
		||||
	if name[0] == '~' {
 | 
			
		||||
		// Means name only contained "~".
 | 
			
		||||
		if len(name) < 2 {
 | 
			
		||||
			return false
 | 
			
		||||
@@ -274,12 +271,12 @@ func IsValidUser(name string) bool {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check to see if the first index is alphanumeric.
 | 
			
		||||
	if (name[0] < 0x41 || name[0] > 0x4A) && (name[0] < 0x61 || name[0] > 0x7A) && (name[0] < 0x30 || name[0] > 0x39) {
 | 
			
		||||
	if (name[0] < 'A' || name[0] > 'J') && (name[0] < 'a' || name[0] > 'z') && (name[0] < '0' || name[0] > '9') {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 1; i < len(name); i++ {
 | 
			
		||||
		if (name[i] < 0x41 || name[i] > 0x7D) && (name[i] < 0x30 || name[i] > 0x39) && name[i] != 0x2D && name[i] != 0x2E {
 | 
			
		||||
		if (name[i] < 'A' || name[i] > '}') && (name[i] < '0' || name[i] > '9') && name[i] != '-' && name[i] != '.' {
 | 
			
		||||
			// a-z, A-Z, 0-9, -, and _\[]{}^|
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
@@ -290,8 +287,13 @@ func IsValidUser(name string) bool {
 | 
			
		||||
 | 
			
		||||
// ToRFC1459 converts a string to the stripped down conversion within RFC
 | 
			
		||||
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
 | 
			
		||||
// and so forth. Useful to compare two nicknames or channels.
 | 
			
		||||
func ToRFC1459(input string) (out string) {
 | 
			
		||||
// and so forth. Useful to compare two nicknames or channels. Note that this
 | 
			
		||||
// should not be used to normalize nicknames or similar, as this may convert
 | 
			
		||||
// valid input characters to non-rfc-valid characters. As such, it's main use
 | 
			
		||||
// is for comparing two nicks.
 | 
			
		||||
func ToRFC1459(input string) string {
 | 
			
		||||
	var out string
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(input); i++ {
 | 
			
		||||
		if input[i] >= 65 && input[i] <= 94 {
 | 
			
		||||
			out += string(rune(input[i]) + 32)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								vendor/github.com/lrstanley/girc/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										131
									
								
								vendor/github.com/lrstanley/girc/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -29,11 +29,12 @@ func (c *Client) RunHandlers(event *Event) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Regular wildcard handlers.
 | 
			
		||||
	c.Handlers.exec(ALL_EVENTS, c, event.Copy())
 | 
			
		||||
	// Background handlers first.
 | 
			
		||||
	c.Handlers.exec(ALL_EVENTS, true, c, event.Copy())
 | 
			
		||||
	c.Handlers.exec(event.Command, true, c, event.Copy())
 | 
			
		||||
 | 
			
		||||
	// Then regular handlers.
 | 
			
		||||
	c.Handlers.exec(event.Command, c, event.Copy())
 | 
			
		||||
	c.Handlers.exec(ALL_EVENTS, false, c, event.Copy())
 | 
			
		||||
	c.Handlers.exec(event.Command, false, c, event.Copy())
 | 
			
		||||
 | 
			
		||||
	// Check if it's a CTCP.
 | 
			
		||||
	if ctcp := decodeCTCP(event.Copy()); ctcp != nil {
 | 
			
		||||
@@ -144,7 +145,7 @@ func (c *Caller) cuid(cmd string, n int) (cuid, uid string) {
 | 
			
		||||
// cuidToID allows easy mapping between a generated cuid and the caller
 | 
			
		||||
// external/internal handler maps.
 | 
			
		||||
func (c *Caller) cuidToID(input string) (cmd, uid string) {
 | 
			
		||||
	i := strings.IndexByte(input, 0x3A)
 | 
			
		||||
	i := strings.IndexByte(input, ':')
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		return "", ""
 | 
			
		||||
	}
 | 
			
		||||
@@ -160,9 +161,9 @@ type execStack struct {
 | 
			
		||||
// exec executes all handlers pertaining to specified event. Internal first,
 | 
			
		||||
// then external.
 | 
			
		||||
//
 | 
			
		||||
// Please note that there is no specific order/priority for which the
 | 
			
		||||
// handler types themselves or the handlers are executed.
 | 
			
		||||
func (c *Caller) exec(command string, client *Client, event *Event) {
 | 
			
		||||
// Please note that there is no specific order/priority for which the handlers
 | 
			
		||||
// are executed.
 | 
			
		||||
func (c *Caller) exec(command string, bg bool, client *Client, event *Event) {
 | 
			
		||||
	// Build a stack of handlers which can be executed concurrently.
 | 
			
		||||
	var stack []execStack
 | 
			
		||||
 | 
			
		||||
@@ -170,13 +171,21 @@ func (c *Caller) exec(command string, client *Client, event *Event) {
 | 
			
		||||
	// Get internal handlers first.
 | 
			
		||||
	if _, ok := c.internal[command]; ok {
 | 
			
		||||
		for cuid := range c.internal[command] {
 | 
			
		||||
			if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stack = append(stack, execStack{c.internal[command][cuid], cuid})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Aaand then external handlers.
 | 
			
		||||
	// Then external handlers.
 | 
			
		||||
	if _, ok := c.external[command]; ok {
 | 
			
		||||
		for cuid := range c.external[command] {
 | 
			
		||||
			if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stack = append(stack, execStack{c.external[command][cuid], cuid})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -189,18 +198,29 @@ func (c *Caller) exec(command string, client *Client, event *Event) {
 | 
			
		||||
	wg.Add(len(stack))
 | 
			
		||||
	for i := 0; i < len(stack); i++ {
 | 
			
		||||
		go func(index int) {
 | 
			
		||||
			c.debug.Printf("executing handler %s for event %s (%d of %d)", stack[index].cuid, command, index+1, len(stack))
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			c.debug.Printf("[%d/%d] exec %s => %s", index+1, len(stack), stack[index].cuid, command)
 | 
			
		||||
			start := time.Now()
 | 
			
		||||
 | 
			
		||||
			// If they want to catch any panics, add to defer stack.
 | 
			
		||||
			if bg {
 | 
			
		||||
				go func() {
 | 
			
		||||
					if client.Config.RecoverFunc != nil {
 | 
			
		||||
						defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					stack[index].Execute(client, *event)
 | 
			
		||||
					c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start))
 | 
			
		||||
				}()
 | 
			
		||||
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if client.Config.RecoverFunc != nil {
 | 
			
		||||
				defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stack[index].Execute(client, *event)
 | 
			
		||||
 | 
			
		||||
			c.debug.Printf("execution of %s took %s (%d of %d)", stack[index].cuid, time.Since(start), index+1, len(stack))
 | 
			
		||||
			wg.Done()
 | 
			
		||||
			c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start))
 | 
			
		||||
		}(i)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -281,9 +301,9 @@ func (c *Caller) remove(cuid string) (success bool) {
 | 
			
		||||
 | 
			
		||||
// sregister is much like Caller.register(), except that it safely locks
 | 
			
		||||
// the Caller mutex.
 | 
			
		||||
func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
func (c *Caller) sregister(internal, bg bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	cuid = c.register(internal, cmd, handler)
 | 
			
		||||
	cuid = c.register(internal, bg, cmd, handler)
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	return cuid
 | 
			
		||||
@@ -291,30 +311,34 @@ func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid str
 | 
			
		||||
 | 
			
		||||
// register will register a handler in the internal tracker. Unsafe (you
 | 
			
		||||
// must lock c.mu yourself!)
 | 
			
		||||
func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
func (c *Caller) register(internal, bg bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
	var uid string
 | 
			
		||||
 | 
			
		||||
	cmd = strings.ToUpper(cmd)
 | 
			
		||||
 | 
			
		||||
	cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
	if bg {
 | 
			
		||||
		uid += ":bg"
 | 
			
		||||
		cuid += ":bg"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if internal {
 | 
			
		||||
		if _, ok := c.internal[cmd]; !ok {
 | 
			
		||||
			c.internal[cmd] = map[string]Handler{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
		c.internal[cmd][uid] = handler
 | 
			
		||||
	} else {
 | 
			
		||||
		if _, ok := c.external[cmd]; !ok {
 | 
			
		||||
			c.external[cmd] = map[string]Handler{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
		c.external[cmd][uid] = handler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, file, line, _ := runtime.Caller(3)
 | 
			
		||||
 | 
			
		||||
	c.debug.Printf("registering handler for %q with cuid %q (internal: %t) from: %s:%d", cmd, cuid, internal, file, line)
 | 
			
		||||
	c.debug.Printf("reg %q => %s [int:%t bg:%t] %s:%d", uid, cmd, internal, bg, file, line)
 | 
			
		||||
 | 
			
		||||
	return cuid
 | 
			
		||||
}
 | 
			
		||||
@@ -323,31 +347,20 @@ func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid stri
 | 
			
		||||
// given event. cuid is the handler uid which can be used to remove the
 | 
			
		||||
// handler with Caller.Remove().
 | 
			
		||||
func (c *Caller) AddHandler(cmd string, handler Handler) (cuid string) {
 | 
			
		||||
	return c.sregister(false, cmd, handler)
 | 
			
		||||
	return c.sregister(false, false, cmd, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add registers the handler function for the given event. cuid is the
 | 
			
		||||
// handler uid which can be used to remove the handler with Caller.Remove().
 | 
			
		||||
func (c *Caller) Add(cmd string, handler func(client *Client, event Event)) (cuid string) {
 | 
			
		||||
	return c.sregister(false, cmd, HandlerFunc(handler))
 | 
			
		||||
	return c.sregister(false, false, cmd, HandlerFunc(handler))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddBg registers the handler function for the given event and executes it
 | 
			
		||||
// in a go-routine. cuid is the handler uid which can be used to remove the
 | 
			
		||||
// handler with Caller.Remove().
 | 
			
		||||
func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (cuid string) {
 | 
			
		||||
	return c.sregister(false, cmd, HandlerFunc(func(client *Client, event Event) {
 | 
			
		||||
		// Setting up background-based handlers this way allows us to get
 | 
			
		||||
		// clean call stacks for use with panic recovery.
 | 
			
		||||
		go func() {
 | 
			
		||||
			// If they want to catch any panics, add to defer stack.
 | 
			
		||||
			if client.Config.RecoverFunc != nil {
 | 
			
		||||
				defer recoverHandlerPanic(client, &event, "goroutine", 3)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			handler(client, event)
 | 
			
		||||
		}()
 | 
			
		||||
	}))
 | 
			
		||||
	return c.sregister(false, true, cmd, HandlerFunc(handler))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddTmp adds a "temporary" handler, which is good for one-time or few-time
 | 
			
		||||
@@ -361,47 +374,37 @@ func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (c
 | 
			
		||||
//
 | 
			
		||||
// Additionally, AddTmp has a useful option, deadline. When set to greater
 | 
			
		||||
// than 0, deadline will be the amount of time that passes before the handler
 | 
			
		||||
// is removed from the stack, regardless if the handler returns true or not.
 | 
			
		||||
// is removed from the stack, regardless of if the handler returns true or not.
 | 
			
		||||
// This is useful in that it ensures that the handler is cleaned up if the
 | 
			
		||||
// server does not respond appropriately, or takes too long to respond.
 | 
			
		||||
//
 | 
			
		||||
// Note that handlers supplied with AddTmp are executed in a goroutine to
 | 
			
		||||
// ensure that they are not blocking other handlers. Additionally, use cuid
 | 
			
		||||
// with Caller.Remove() to prematurely remove the handler from the stack,
 | 
			
		||||
// bypassing the timeout or waiting for the handler to return that it wants
 | 
			
		||||
// to be removed from the stack.
 | 
			
		||||
// ensure that they are not blocking other handlers. However, if you are
 | 
			
		||||
// creating a temporary handler from another handler, it should be a
 | 
			
		||||
// background handler.
 | 
			
		||||
//
 | 
			
		||||
// Use cuid with Caller.Remove() to prematurely remove the handler from the
 | 
			
		||||
// stack, bypassing the timeout or waiting for the handler to return that it
 | 
			
		||||
// wants to be removed from the stack.
 | 
			
		||||
func (c *Caller) AddTmp(cmd string, deadline time.Duration, handler func(client *Client, event Event) bool) (cuid string, done chan struct{}) {
 | 
			
		||||
	var uid string
 | 
			
		||||
	cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
 | 
			
		||||
	done = make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	if _, ok := c.external[cmd]; !ok {
 | 
			
		||||
		c.external[cmd] = map[string]Handler{}
 | 
			
		||||
	}
 | 
			
		||||
	c.external[cmd][uid] = HandlerFunc(func(client *Client, event Event) {
 | 
			
		||||
		// Setting up background-based handlers this way allows us to get
 | 
			
		||||
		// clean call stacks for use with panic recovery.
 | 
			
		||||
		go func() {
 | 
			
		||||
			// If they want to catch any panics, add to defer stack.
 | 
			
		||||
			if client.Config.RecoverFunc != nil {
 | 
			
		||||
				defer recoverHandlerPanic(client, &event, "tmp-goroutine", 3)
 | 
			
		||||
	cuid = c.sregister(false, true, cmd, HandlerFunc(func(client *Client, event Event) {
 | 
			
		||||
		remove := handler(client, event)
 | 
			
		||||
		if remove {
 | 
			
		||||
			if ok := c.Remove(cuid); ok {
 | 
			
		||||
				close(done)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			remove := handler(client, event)
 | 
			
		||||
			if remove {
 | 
			
		||||
				if ok := c.Remove(cuid); ok {
 | 
			
		||||
					close(done)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
	})
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
 | 
			
		||||
	if deadline > 0 {
 | 
			
		||||
		go func() {
 | 
			
		||||
			<-time.After(deadline)
 | 
			
		||||
			select {
 | 
			
		||||
			case <-time.After(deadline):
 | 
			
		||||
			case <-done:
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ok := c.Remove(cuid); ok {
 | 
			
		||||
				close(done)
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/lrstanley/girc/modes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/lrstanley/girc/modes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -206,11 +206,11 @@ func (c *CModes) Parse(flags string, args []string) (out []CMode) {
 | 
			
		||||
	var argCount int
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(flags); i++ {
 | 
			
		||||
		if flags[i] == 0x2B {
 | 
			
		||||
		if flags[i] == '+' {
 | 
			
		||||
			add = true
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if flags[i] == 0x2D {
 | 
			
		||||
		if flags[i] == '-' {
 | 
			
		||||
			add = false
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -265,7 +265,7 @@ func IsValidChannelMode(raw string) bool {
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(raw); i++ {
 | 
			
		||||
		// Allowed are: ",", A-Z and a-z.
 | 
			
		||||
		if raw[i] != 0x2C && (raw[i] < 0x41 || raw[i] > 0x5A) && (raw[i] < 0x61 || raw[i] > 0x7A) {
 | 
			
		||||
		if raw[i] != ',' && (raw[i] < 'A' || raw[i] > 'Z') && (raw[i] < 'a' || raw[i] > 'z') {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -279,7 +279,7 @@ func isValidUserPrefix(raw string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if raw[0] != 0x28 { // (.
 | 
			
		||||
	if raw[0] != '(' {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -288,7 +288,7 @@ func isValidUserPrefix(raw string) bool {
 | 
			
		||||
 | 
			
		||||
	// Skip the first one as we know it's (.
 | 
			
		||||
	for i := 1; i < len(raw); i++ {
 | 
			
		||||
		if raw[i] == 0x29 { // ).
 | 
			
		||||
		if raw[i] == ')' {
 | 
			
		||||
			passedKeys = true
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								vendor/github.com/matrix-org/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										90
									
								
								vendor/github.com/matrix-org/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -79,7 +79,7 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string {
 | 
			
		||||
	return hsURL.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildURLWithQuery builds a URL with query paramters in addition to the Client's homeserver/prefix/access_token set already.
 | 
			
		||||
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already.
 | 
			
		||||
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
 | 
			
		||||
	u, _ := url.Parse(cli.BuildURL(urlPath...))
 | 
			
		||||
	q := u.Query()
 | 
			
		||||
@@ -387,6 +387,20 @@ func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", mxid, "displayname")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) SetDisplayName(displayName string) (err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
 | 
			
		||||
@@ -450,6 +464,35 @@ func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
		TextMessage{"m.text", text})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendImage sends an m.room.message event into the given room with a msgtype of m.image
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
 | 
			
		||||
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		ImageMessage{
 | 
			
		||||
			MsgType: "m.image",
 | 
			
		||||
			Body:    body,
 | 
			
		||||
			URL:     url,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		VideoMessage{
 | 
			
		||||
			MsgType: "m.video",
 | 
			
		||||
			Body:    body,
 | 
			
		||||
			URL:     url,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
 | 
			
		||||
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		TextMessage{"m.notice", text})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
 | 
			
		||||
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
 | 
			
		||||
	txnID := txnID()
 | 
			
		||||
@@ -518,6 +561,14 @@ func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanU
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
 | 
			
		||||
	req := ReqTyping{Typing: typing, Timeout: timeout}
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
 | 
			
		||||
// the HTTP response body, or return an error.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
 | 
			
		||||
@@ -556,8 +607,15 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if res.StatusCode != 200 {
 | 
			
		||||
		contents, err := ioutil.ReadAll(res.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, HTTPError{
 | 
			
		||||
				Message: "Upload request failed - Failed to read response body: " + err.Error(),
 | 
			
		||||
				Code:    res.StatusCode,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil, HTTPError{
 | 
			
		||||
			Message: "Upload request failed",
 | 
			
		||||
			Message: "Upload request failed: " + string(contents),
 | 
			
		||||
			Code:    res.StatusCode,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -588,6 +646,34 @@ func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messages returns a list of message and state events for a room. It uses
 | 
			
		||||
// pagination query parameters to paginate history in the room.
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
 | 
			
		||||
func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"from": from,
 | 
			
		||||
		"dir":  string(dir),
 | 
			
		||||
	}
 | 
			
		||||
	if to != "" {
 | 
			
		||||
		query["to"] = to
 | 
			
		||||
	}
 | 
			
		||||
	if limit != 0 {
 | 
			
		||||
		query["limit"] = strconv.Itoa(limit)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TurnServer returns turn server details and credentials for the client to use when initiating calls.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
 | 
			
		||||
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("voip", "turnServer")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func txnID() string {
 | 
			
		||||
	return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,13 +7,13 @@ import (
 | 
			
		||||
 | 
			
		||||
// Event represents a single Matrix event.
 | 
			
		||||
type Event struct {
 | 
			
		||||
	StateKey  string                 `json:"state_key"`        // The state key for the event. Only present on State Events.
 | 
			
		||||
	Sender    string                 `json:"sender"`           // The user ID of the sender of the event
 | 
			
		||||
	Type      string                 `json:"type"`             // The event type
 | 
			
		||||
	Timestamp int                    `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
 | 
			
		||||
	ID        string                 `json:"event_id"`         // The unique ID of this event
 | 
			
		||||
	RoomID    string                 `json:"room_id"`          // The room the event was sent to. May be nil (e.g. for presence)
 | 
			
		||||
	Content   map[string]interface{} `json:"content"`          // The JSON content of the event.
 | 
			
		||||
	StateKey  *string                `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
 | 
			
		||||
	Sender    string                 `json:"sender"`              // The user ID of the sender of the event
 | 
			
		||||
	Type      string                 `json:"type"`                // The event type
 | 
			
		||||
	Timestamp int64                  `json:"origin_server_ts"`    // The unix timestamp when this message was sent by the origin server
 | 
			
		||||
	ID        string                 `json:"event_id"`            // The unique ID of this event
 | 
			
		||||
	RoomID    string                 `json:"room_id"`             // The room the event was sent to. May be nil (e.g. for presence)
 | 
			
		||||
	Content   map[string]interface{} `json:"content"`             // The JSON content of the event.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Body returns the value of the "body" key in the event content if it is
 | 
			
		||||
@@ -44,12 +44,31 @@ type TextMessage struct {
 | 
			
		||||
	Body    string `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageInfo contains info about an image
 | 
			
		||||
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
 | 
			
		||||
type ImageInfo struct {
 | 
			
		||||
	Height   uint   `json:"h"`
 | 
			
		||||
	Width    uint   `json:"w"`
 | 
			
		||||
	Mimetype string `json:"mimetype"`
 | 
			
		||||
	Size     uint   `json:"size"`
 | 
			
		||||
	Height   uint   `json:"h,omitempty"`
 | 
			
		||||
	Width    uint   `json:"w,omitempty"`
 | 
			
		||||
	Mimetype string `json:"mimetype,omitempty"`
 | 
			
		||||
	Size     uint   `json:"size,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
type VideoInfo struct {
 | 
			
		||||
	Mimetype      string    `json:"mimetype,omitempty"`
 | 
			
		||||
	ThumbnailInfo ImageInfo `json:"thumbnail_info"`
 | 
			
		||||
	ThumbnailURL  string    `json:"thumbnail_url,omitempty"`
 | 
			
		||||
	Height        uint      `json:"h,omitempty"`
 | 
			
		||||
	Width         uint      `json:"w,omitempty"`
 | 
			
		||||
	Duration      uint      `json:"duration,omitempty"`
 | 
			
		||||
	Size          uint      `json:"size,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VideoMessage is an m.video  - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
type VideoMessage struct {
 | 
			
		||||
	MsgType string    `json:"msgtype"`
 | 
			
		||||
	Body    string    `json:"body"`
 | 
			
		||||
	URL     string    `json:"url"`
 | 
			
		||||
	Info    VideoInfo `json:"info"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageMessage is an m.image event
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
// Copyright 2017 Jan Christian Grünhage
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
 | 
			
		||||
//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
 | 
			
		||||
type Filter struct {
 | 
			
		||||
	AccountData FilterPart `json:"account_data,omitempty"`
 | 
			
		||||
	EventFields []string   `json:"event_fields,omitempty"`
 | 
			
		||||
	EventFormat string     `json:"event_format,omitempty"`
 | 
			
		||||
	Presence    FilterPart `json:"presence,omitempty"`
 | 
			
		||||
	Room        struct {
 | 
			
		||||
		AccountData  FilterPart `json:"account_data,omitempty"`
 | 
			
		||||
		Ephemeral    FilterPart `json:"ephemeral,omitempty"`
 | 
			
		||||
		IncludeLeave bool       `json:"include_leave,omitempty"`
 | 
			
		||||
		NotRooms     []string   `json:"not_rooms,omitempty"`
 | 
			
		||||
		Rooms        []string   `json:"rooms,omitempty"`
 | 
			
		||||
		State        FilterPart `json:"state,omitempty"`
 | 
			
		||||
		Timeline     FilterPart `json:"timeline,omitempty"`
 | 
			
		||||
	} `json:"room,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FilterPart struct {
 | 
			
		||||
	NotRooms   []string `json:"not_rooms,omitempty"`
 | 
			
		||||
	Rooms      []string `json:"rooms,omitempty"`
 | 
			
		||||
	Limit      *int     `json:"limit,omitempty"`
 | 
			
		||||
	NotSenders []string `json:"not_senders,omitempty"`
 | 
			
		||||
	NotTypes   []string `json:"not_types,omitempty"`
 | 
			
		||||
	Senders    []string `json:"senders,omitempty"`
 | 
			
		||||
	Types      []string `json:"types,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/matrix-org/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/matrix-org/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -70,3 +70,9 @@ type ReqBanUser struct {
 | 
			
		||||
type ReqUnbanUser struct {
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
type ReqTyping struct {
 | 
			
		||||
	Typing  bool  `json:"typing"`
 | 
			
		||||
	Timeout int64 `json:"timeout"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/matrix-org/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/matrix-org/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -45,6 +45,9 @@ type RespBanUser struct{}
 | 
			
		||||
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
type RespUnbanUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
type RespTyping struct{}
 | 
			
		||||
 | 
			
		||||
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
type RespJoinedRooms struct {
 | 
			
		||||
	JoinedRooms []string `json:"joined_rooms"`
 | 
			
		||||
@@ -58,6 +61,13 @@ type RespJoinedMembers struct {
 | 
			
		||||
	} `json:"joined"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
 | 
			
		||||
type RespMessages struct {
 | 
			
		||||
	Start string  `json:"start"`
 | 
			
		||||
	Chunk []Event `json:"chunk"`
 | 
			
		||||
	End   string  `json:"end"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
 | 
			
		||||
type RespSendEvent struct {
 | 
			
		||||
	EventID string `json:"event_id"`
 | 
			
		||||
@@ -90,6 +100,11 @@ func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
type RespUserDisplayName struct {
 | 
			
		||||
	DisplayName string `json:"displayname"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
type RespRegister struct {
 | 
			
		||||
	AccessToken  string `json:"access_token"`
 | 
			
		||||
@@ -125,6 +140,16 @@ type RespSync struct {
 | 
			
		||||
		Events []Event `json:"events"`
 | 
			
		||||
	} `json:"presence"`
 | 
			
		||||
	Rooms struct {
 | 
			
		||||
		Leave map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event `json:"events"`
 | 
			
		||||
			} `json:"state"`
 | 
			
		||||
			Timeline struct {
 | 
			
		||||
				Events    []Event `json:"events"`
 | 
			
		||||
				Limited   bool    `json:"limited"`
 | 
			
		||||
				PrevBatch string  `json:"prev_batch"`
 | 
			
		||||
			} `json:"timeline"`
 | 
			
		||||
		} `json:"leave"`
 | 
			
		||||
		Join map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event `json:"events"`
 | 
			
		||||
@@ -142,3 +167,10 @@ type RespSync struct {
 | 
			
		||||
		} `json:"invite"`
 | 
			
		||||
	} `json:"rooms"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RespTurnServer struct {
 | 
			
		||||
	Username string   `json:"username"`
 | 
			
		||||
	Password string   `json:"password"`
 | 
			
		||||
	TTL      int      `json:"ttl"`
 | 
			
		||||
	URIs     []string `json:"uris"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/matrix-org/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/matrix-org/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,7 +13,7 @@ func (room Room) UpdateState(event *Event) {
 | 
			
		||||
	if !exists {
 | 
			
		||||
		room.State[event.Type] = make(map[string]*Event)
 | 
			
		||||
	}
 | 
			
		||||
	room.State[event.Type][event.StateKey] = event
 | 
			
		||||
	room.State[event.Type][*event.StateKey] = event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/matrix-org/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/matrix-org/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -73,6 +73,16 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error)
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Leave {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.Timeline.Events {
 | 
			
		||||
			if event.StateKey != nil {
 | 
			
		||||
				event.RoomID = roomID
 | 
			
		||||
				room.UpdateState(&event)
 | 
			
		||||
				s.notifyListeners(&event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +112,7 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool
 | 
			
		||||
	for roomID, roomData := range resp.Rooms.Join {
 | 
			
		||||
		for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
 | 
			
		||||
			e := roomData.Timeline.Events[i]
 | 
			
		||||
			if e.Type == "m.room.member" && e.StateKey == s.UserID {
 | 
			
		||||
			if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
 | 
			
		||||
				m := e.Content["membership"]
 | 
			
		||||
				mship, ok := m.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/matterbridge/slack/websocket_utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/matterbridge/slack/websocket_utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,20 +0,0 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var portMapping = map[string]string{"ws": "80", "wss": "443"}
 | 
			
		||||
 | 
			
		||||
func websocketizeURLPort(orig string) (string, error) {
 | 
			
		||||
	urlObj, err := url.ParseRequestURI(orig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = net.SplitHostPort(urlObj.Host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return urlObj.Scheme + "://" + urlObj.Host + ":" + portMapping[urlObj.Scheme] + urlObj.Path, nil
 | 
			
		||||
	}
 | 
			
		||||
	return orig, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -25,6 +25,7 @@ type AttachmentAction struct {
 | 
			
		||||
	SelectedOptions []AttachmentActionOption      `json:"selected_options,omitempty"` // Optional. The first element of this array will be set as the pre-selected option for this menu.
 | 
			
		||||
	OptionGroups    []AttachmentActionOptionGroup `json:"option_groups,omitempty"`    // Optional.
 | 
			
		||||
	Confirm         *ConfirmationField            `json:"confirm,omitempty"`          // Optional.
 | 
			
		||||
	URL             string                        `json:"url,omitempty"`              // Optional.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttachmentActionOption the individual option to appear in action menu.
 | 
			
		||||
@@ -48,6 +49,9 @@ type AttachmentActionCallback struct {
 | 
			
		||||
	Channel    Channel            `json:"channel"`
 | 
			
		||||
	User       User               `json:"user"`
 | 
			
		||||
 | 
			
		||||
	Name  string `json:"name"`
 | 
			
		||||
	Value string `json:"value"`
 | 
			
		||||
 | 
			
		||||
	OriginalMessage Message `json:"original_message"`
 | 
			
		||||
 | 
			
		||||
	ActionTs     string `json:"action_ts"`
 | 
			
		||||
@@ -38,51 +38,51 @@ func channelRequest(ctx context.Context, path string, values url.Values, debug b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveChannel archives the given channel
 | 
			
		||||
func (api *Client) ArchiveChannel(channel string) error {
 | 
			
		||||
	return api.ArchiveChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.archive
 | 
			
		||||
func (api *Client) ArchiveChannel(channelID string) error {
 | 
			
		||||
	return api.ArchiveChannelContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveChannelContext archives the given channel with a custom context
 | 
			
		||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channel string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.archive
 | 
			
		||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.archive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveChannel unarchives the given channel
 | 
			
		||||
func (api *Client) UnarchiveChannel(channel string) error {
 | 
			
		||||
	return api.UnarchiveChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.unarchive
 | 
			
		||||
func (api *Client) UnarchiveChannel(channelID string) error {
 | 
			
		||||
	return api.UnarchiveChannelContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveChannelContext unarchives the given channel with a custom context
 | 
			
		||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channel string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.unarchive
 | 
			
		||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.unarchive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateChannel creates a channel with the given name and returns a *Channel
 | 
			
		||||
func (api *Client) CreateChannel(channel string) (*Channel, error) {
 | 
			
		||||
	return api.CreateChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.create
 | 
			
		||||
func (api *Client) CreateChannel(channelName string) (*Channel, error) {
 | 
			
		||||
	return api.CreateChannelContext(context.Background(), channelName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateChannelContext creates a channel with the given name and returns a *Channel with a custom context
 | 
			
		||||
func (api *Client) CreateChannelContext(ctx context.Context, channel string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.create
 | 
			
		||||
func (api *Client) CreateChannelContext(ctx context.Context, channelName string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
		"name":  {channelName},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.create", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -92,15 +92,17 @@ func (api *Client) CreateChannelContext(ctx context.Context, channel string) (*C
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelHistory retrieves the channel history
 | 
			
		||||
func (api *Client) GetChannelHistory(channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	return api.GetChannelHistoryContext(context.Background(), channel, params)
 | 
			
		||||
// see https://api.slack.com/methods/channels.history
 | 
			
		||||
func (api *Client) GetChannelHistory(channelID string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	return api.GetChannelHistoryContext(context.Background(), channelID, params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelHistoryContext retrieves the channel history with a custom context
 | 
			
		||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.history
 | 
			
		||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Latest != DEFAULT_HISTORY_LATEST {
 | 
			
		||||
		values.Add("latest", params.Latest)
 | 
			
		||||
@@ -133,15 +135,17 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelInfo retrieves the given channel
 | 
			
		||||
func (api *Client) GetChannelInfo(channel string) (*Channel, error) {
 | 
			
		||||
	return api.GetChannelInfoContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.info
 | 
			
		||||
func (api *Client) GetChannelInfo(channelID string) (*Channel, error) {
 | 
			
		||||
	return api.GetChannelInfoContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelInfoContext retrieves the given channel with a custom context
 | 
			
		||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channel string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.info
 | 
			
		||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -151,15 +155,17 @@ func (api *Client) GetChannelInfoContext(ctx context.Context, channel string) (*
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToChannel invites a user to a given channel and returns a *Channel
 | 
			
		||||
func (api *Client) InviteUserToChannel(channel, user string) (*Channel, error) {
 | 
			
		||||
	return api.InviteUserToChannelContext(context.Background(), channel, user)
 | 
			
		||||
// see https://api.slack.com/methods/channels.invite
 | 
			
		||||
func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error) {
 | 
			
		||||
	return api.InviteUserToChannelContext(context.Background(), channelID, user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToChannelCustom invites a user to a given channel and returns a *Channel with a custom context
 | 
			
		||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channel, user string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.invite
 | 
			
		||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.invite", values, api.debug)
 | 
			
		||||
@@ -170,15 +176,17 @@ func (api *Client) InviteUserToChannelContext(ctx context.Context, channel, user
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinChannel joins the currently authenticated user to a channel
 | 
			
		||||
func (api *Client) JoinChannel(channel string) (*Channel, error) {
 | 
			
		||||
	return api.JoinChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.join
 | 
			
		||||
func (api *Client) JoinChannel(channelName string) (*Channel, error) {
 | 
			
		||||
	return api.JoinChannelContext(context.Background(), channelName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinChannelContext joins the currently authenticated user to a channel with a custom context
 | 
			
		||||
func (api *Client) JoinChannelContext(ctx context.Context, channel string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.join
 | 
			
		||||
func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
		"name":  {channelName},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.join", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -188,15 +196,17 @@ func (api *Client) JoinChannelContext(ctx context.Context, channel string) (*Cha
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveChannel makes the authenticated user leave the given channel
 | 
			
		||||
func (api *Client) LeaveChannel(channel string) (bool, error) {
 | 
			
		||||
	return api.LeaveChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.leave
 | 
			
		||||
func (api *Client) LeaveChannel(channelID string) (bool, error) {
 | 
			
		||||
	return api.LeaveChannelContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveChannelContext makes the authenticated user leave the given channel with a custom context
 | 
			
		||||
func (api *Client) LeaveChannelContext(ctx context.Context, channel string) (bool, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.leave
 | 
			
		||||
func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -209,30 +219,31 @@ func (api *Client) LeaveChannelContext(ctx context.Context, channel string) (boo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromChannel kicks a user from a given channel
 | 
			
		||||
func (api *Client) KickUserFromChannel(channel, user string) error {
 | 
			
		||||
	return api.KickUserFromChannelContext(context.Background(), channel, user)
 | 
			
		||||
// see https://api.slack.com/methods/channels.kick
 | 
			
		||||
func (api *Client) KickUserFromChannel(channelID, user string) error {
 | 
			
		||||
	return api.KickUserFromChannelContext(context.Background(), channelID, user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromChannelContext kicks a user from a given channel with a custom context
 | 
			
		||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channel, user string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.kick
 | 
			
		||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannels retrieves all the channels
 | 
			
		||||
// see https://api.slack.com/methods/channels.list
 | 
			
		||||
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
 | 
			
		||||
	return api.GetChannelsContext(context.Background(), excludeArchived)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelsContext retrieves all the channels with a custom context
 | 
			
		||||
// see https://api.slack.com/methods/channels.list
 | 
			
		||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) ([]Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
@@ -252,35 +263,36 @@ func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool)
 | 
			
		||||
// 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 {
 | 
			
		||||
	return api.SetChannelReadMarkContext(context.Background(), channel, ts)
 | 
			
		||||
// see https://api.slack.com/methods/channels.mark
 | 
			
		||||
func (api *Client) SetChannelReadMark(channelID, ts string) error {
 | 
			
		||||
	return api.SetChannelReadMarkContext(context.Background(), channelID, ts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelReadMarkContext sets the read mark of a given channel to a specific point with a custom context
 | 
			
		||||
// For more details see SetChannelReadMark documentation
 | 
			
		||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channel, ts string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.mark
 | 
			
		||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameChannel renames a given channel
 | 
			
		||||
func (api *Client) RenameChannel(channel, name string) (*Channel, error) {
 | 
			
		||||
	return api.RenameChannelContext(context.Background(), channel, name)
 | 
			
		||||
// see https://api.slack.com/methods/channels.rename
 | 
			
		||||
func (api *Client) RenameChannel(channelID, name string) (*Channel, error) {
 | 
			
		||||
	return api.RenameChannelContext(context.Background(), channelID, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameChannelContext renames a given channel with a custom context
 | 
			
		||||
func (api *Client) RenameChannelContext(ctx context.Context, channel, name string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.rename
 | 
			
		||||
func (api *Client) RenameChannelContext(ctx context.Context, channelID, name string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"name":    {name},
 | 
			
		||||
	}
 | 
			
		||||
	// XXX: the created entry in this call returns a string instead of a number
 | 
			
		||||
@@ -293,15 +305,17 @@ func (api *Client) RenameChannelContext(ctx context.Context, channel, name strin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelPurpose sets the channel purpose and returns the purpose that was successfully set
 | 
			
		||||
func (api *Client) SetChannelPurpose(channel, purpose string) (string, error) {
 | 
			
		||||
	return api.SetChannelPurposeContext(context.Background(), channel, purpose)
 | 
			
		||||
// see https://api.slack.com/methods/channels.setPurpose
 | 
			
		||||
func (api *Client) SetChannelPurpose(channelID, purpose string) (string, error) {
 | 
			
		||||
	return api.SetChannelPurposeContext(context.Background(), channelID, purpose)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelPurposeContext sets the channel purpose and returns the purpose that was successfully set with a custom context
 | 
			
		||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channel, purpose string) (string, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.setPurpose
 | 
			
		||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purpose string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"purpose": {purpose},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.setPurpose", values, api.debug)
 | 
			
		||||
@@ -312,15 +326,17 @@ func (api *Client) SetChannelPurposeContext(ctx context.Context, channel, purpos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelTopic sets the channel topic and returns the topic that was successfully set
 | 
			
		||||
func (api *Client) SetChannelTopic(channel, topic string) (string, error) {
 | 
			
		||||
	return api.SetChannelTopicContext(context.Background(), channel, topic)
 | 
			
		||||
// see https://api.slack.com/methods/channels.setTopic
 | 
			
		||||
func (api *Client) SetChannelTopic(channelID, topic string) (string, error) {
 | 
			
		||||
	return api.SetChannelTopicContext(context.Background(), channelID, topic)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelTopicContext sets the channel topic and returns the topic that was successfully set with a custom context
 | 
			
		||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channel, topic string) (string, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.setTopic
 | 
			
		||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"topic":   {topic},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.setTopic", values, api.debug)
 | 
			
		||||
@@ -331,15 +347,17 @@ func (api *Client) SetChannelTopicContext(ctx context.Context, channel, topic st
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelReplies gets an entire thread (a message plus all the messages in reply to it).
 | 
			
		||||
func (api *Client) GetChannelReplies(channel, thread_ts string) ([]Message, error) {
 | 
			
		||||
	return api.GetChannelRepliesContext(context.Background(), channel, thread_ts)
 | 
			
		||||
// see https://api.slack.com/methods/channels.replies
 | 
			
		||||
func (api *Client) GetChannelReplies(channelID, thread_ts string) ([]Message, error) {
 | 
			
		||||
	return api.GetChannelRepliesContext(context.Background(), channelID, thread_ts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelRepliesContext gets an entire thread (a message plus all the messages in reply to it) with a custom context
 | 
			
		||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channel, thread_ts string) ([]Message, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.replies
 | 
			
		||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thread_ts string) ([]Message, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":     {api.config.token},
 | 
			
		||||
		"channel":   {channel},
 | 
			
		||||
		"channel":   {channelID},
 | 
			
		||||
		"thread_ts": {thread_ts},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.replies", values, api.debug)
 | 
			
		||||
							
								
								
									
										62
									
								
								vendor/github.com/matterbridge/slack/chat.go → vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/matterbridge/slack/chat.go → vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,6 +11,7 @@ import (
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_MESSAGE_USERNAME         = ""
 | 
			
		||||
	DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
 | 
			
		||||
	DEFAULT_MESSAGE_REPLY_BROADCAST  = false
 | 
			
		||||
	DEFAULT_MESSAGE_ASUSER           = false
 | 
			
		||||
	DEFAULT_MESSAGE_PARSE            = ""
 | 
			
		||||
	DEFAULT_MESSAGE_LINK_NAMES       = 0
 | 
			
		||||
@@ -36,6 +37,7 @@ type PostMessageParameters struct {
 | 
			
		||||
	AsUser          bool         `json:"as_user"`
 | 
			
		||||
	Parse           string       `json:"parse"`
 | 
			
		||||
	ThreadTimestamp string       `json:"thread_ts"`
 | 
			
		||||
	ReplyBroadcast  bool         `json:"reply_broadcast"`
 | 
			
		||||
	LinkNames       int          `json:"link_names"`
 | 
			
		||||
	Attachments     []Attachment `json:"attachments"`
 | 
			
		||||
	UnfurlLinks     bool         `json:"unfurl_links"`
 | 
			
		||||
@@ -44,12 +46,17 @@ type PostMessageParameters struct {
 | 
			
		||||
	IconEmoji       string       `json:"icon_emoji"`
 | 
			
		||||
	Markdown        bool         `json:"mrkdwn,omitempty"`
 | 
			
		||||
	EscapeText      bool         `json:"escape_text"`
 | 
			
		||||
 | 
			
		||||
	// chat.postEphemeral support
 | 
			
		||||
	Channel string `json:"channel"`
 | 
			
		||||
	User    string `json:"user"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
 | 
			
		||||
func NewPostMessageParameters() PostMessageParameters {
 | 
			
		||||
	return PostMessageParameters{
 | 
			
		||||
		Username:    DEFAULT_MESSAGE_USERNAME,
 | 
			
		||||
		User:        DEFAULT_MESSAGE_USERNAME,
 | 
			
		||||
		AsUser:      DEFAULT_MESSAGE_ASUSER,
 | 
			
		||||
		Parse:       DEFAULT_MESSAGE_PARSE,
 | 
			
		||||
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
 | 
			
		||||
@@ -102,6 +109,37 @@ func (api *Client) PostMessageContext(ctx context.Context, channel, text string,
 | 
			
		||||
	return respChannel, respTimestamp, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostEphemeral sends an ephemeral message to a user in 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) PostEphemeral(channel, userID string, options ...MsgOption) (string, error) {
 | 
			
		||||
	options = append(options, MsgOptionPostEphemeral())
 | 
			
		||||
	return api.PostEphemeralContext(
 | 
			
		||||
		context.Background(),
 | 
			
		||||
		channel,
 | 
			
		||||
		userID,
 | 
			
		||||
		options...,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
 | 
			
		||||
// For more details, see PostEphemeral documentation
 | 
			
		||||
func (api *Client) PostEphemeralContext(ctx context.Context, channel, userID string, options ...MsgOption) (string, error) {
 | 
			
		||||
	path, values, err := ApplyMsgOptions(api.config.token, channel, options...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	values.Add("user", userID)
 | 
			
		||||
 | 
			
		||||
	response, err := chatRequest(ctx, path, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return response.Timestamp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateMessage updates a message in a channel
 | 
			
		||||
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
 | 
			
		||||
	return api.UpdateMessageContext(context.Background(), channel, timestamp, text)
 | 
			
		||||
@@ -171,9 +209,10 @@ func chatRequest(ctx context.Context, path string, values url.Values, debug bool
 | 
			
		||||
type sendMode string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	chatUpdate      sendMode = "chat.update"
 | 
			
		||||
	chatPostMessage sendMode = "chat.postMessage"
 | 
			
		||||
	chatDelete      sendMode = "chat.delete"
 | 
			
		||||
	chatUpdate        sendMode = "chat.update"
 | 
			
		||||
	chatPostMessage   sendMode = "chat.postMessage"
 | 
			
		||||
	chatDelete        sendMode = "chat.delete"
 | 
			
		||||
	chatPostEphemeral sendMode = "chat.postEphemeral"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type sendConfig struct {
 | 
			
		||||
@@ -193,6 +232,15 @@ func MsgOptionPost() MsgOption {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgOptionPostEphemeral posts an ephemeral message
 | 
			
		||||
func MsgOptionPostEphemeral() MsgOption {
 | 
			
		||||
	return func(config *sendConfig) error {
 | 
			
		||||
		config.mode = chatPostEphemeral
 | 
			
		||||
		config.values.Del("ts")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgOptionUpdate updates a message based on the timestamp.
 | 
			
		||||
func MsgOptionUpdate(timestamp string) MsgOption {
 | 
			
		||||
	return func(config *sendConfig) error {
 | 
			
		||||
@@ -279,6 +327,11 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
 | 
			
		||||
			config.values.Set("username", string(params.Username))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// chat.postEphemeral support
 | 
			
		||||
		if params.User != DEFAULT_MESSAGE_USERNAME {
 | 
			
		||||
			config.values.Set("user", params.User)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// never generates an error.
 | 
			
		||||
		MsgOptionAsUser(params.AsUser)(config)
 | 
			
		||||
 | 
			
		||||
@@ -314,6 +367,9 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
 | 
			
		||||
		if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
 | 
			
		||||
			config.values.Set("thread_ts", params.ThreadTimestamp)
 | 
			
		||||
		}
 | 
			
		||||
		if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
 | 
			
		||||
			config.values.Set("reply_broadcast", "true")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
							
								
								
									
										0
									
								
								vendor/github.com/matterbridge/slack/dnd.go → vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								vendor/github.com/matterbridge/slack/dnd.go → vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
								
								
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
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.Name)
 | 
			
		||||
		// channel is of type conversation & groupConversation
 | 
			
		||||
		// see all available methods in `conversation.go`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/ims/ims.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/ims/ims.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
 | 
			
		||||
	userID := "USER_ID"
 | 
			
		||||
 | 
			
		||||
	_, _, channelID, err := api.OpenIMChannel(userID)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.PostMessage(channelID, "Hello World!", slack.PostMessageParameters{})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/nlopes/slack/examples/team/team.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/nlopes/slack/examples/team/team.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	//Example for single user
 | 
			
		||||
	billingActive, err := api.GetBillableInfo("U023BECGF")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("ID: U023BECGF, BillingActive: %v\n\n\n", billingActive["U023BECGF"])
 | 
			
		||||
 | 
			
		||||
	//Example for team
 | 
			
		||||
	billingActiveForTeam, _ := api.GetBillableInfoForTeam()
 | 
			
		||||
	for id, value := range billingActiveForTeam {
 | 
			
		||||
		fmt.Printf("ID: %v, BillingActive: %v\n", id, value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
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()
 | 
			
		||||
 | 
			
		||||
	for msg := range 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 C2147483705 with your Channel ID
 | 
			
		||||
			rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", "C2147483705"))
 | 
			
		||||
 | 
			
		||||
		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")
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
 | 
			
		||||
			// Ignore other events..
 | 
			
		||||
			// fmt.Printf("Unexpected: %v\n", msg.Data)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -267,10 +267,7 @@ func (api *Client) DeleteFileContext(ctx context.Context, fileID string) error {
 | 
			
		||||
		"file":  {fileID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := fileRequest(ctx, "files.delete", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -208,10 +208,7 @@ func (api *Client) LeaveGroupContext(ctx context.Context, group string) error {
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest(ctx, "groups.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromGroup kicks a user from a group
 | 
			
		||||
@@ -227,10 +224,7 @@ func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user str
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest(ctx, "groups.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGroups retrieves all groups
 | 
			
		||||
@@ -289,10 +283,7 @@ func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest(ctx, "groups.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenGroup opens a private group
 | 
			
		||||
							
								
								
									
										0
									
								
								vendor/github.com/matterbridge/slack/im.go → vendor/github.com/nlopes/slack/im.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								vendor/github.com/matterbridge/slack/im.go → vendor/github.com/nlopes/slack/im.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,6 +3,7 @@ package slack
 | 
			
		||||
// OutgoingMessage is used for the realtime API, and seems incomplete.
 | 
			
		||||
type OutgoingMessage struct {
 | 
			
		||||
	ID              int    `json:"id"`
 | 
			
		||||
	// channel ID
 | 
			
		||||
	Channel         string `json:"channel,omitempty"`
 | 
			
		||||
	Text            string `json:"text,omitempty"`
 | 
			
		||||
	Type            string `json:"type,omitempty"`
 | 
			
		||||
@@ -121,12 +122,12 @@ type Pong struct {
 | 
			
		||||
// NewOutgoingMessage prepares an OutgoingMessage that the user can
 | 
			
		||||
// use to send a message. Use this function to properly set the
 | 
			
		||||
// messageID.
 | 
			
		||||
func (rtm *RTM) NewOutgoingMessage(text string, channel string) *OutgoingMessage {
 | 
			
		||||
func (rtm *RTM) NewOutgoingMessage(text string, channelID string) *OutgoingMessage {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	return &OutgoingMessage{
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		Type:    "message",
 | 
			
		||||
		Channel: channel,
 | 
			
		||||
		Channel: channelID,
 | 
			
		||||
		Text:    text,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -134,11 +135,11 @@ func (rtm *RTM) NewOutgoingMessage(text string, channel string) *OutgoingMessage
 | 
			
		||||
// NewTypingMessage prepares an OutgoingMessage that the user can
 | 
			
		||||
// use to send as a typing indicator. Use this function to properly set the
 | 
			
		||||
// messageID.
 | 
			
		||||
func (rtm *RTM) NewTypingMessage(channel string) *OutgoingMessage {
 | 
			
		||||
func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	return &OutgoingMessage{
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		Type:    "typing",
 | 
			
		||||
		Channel: channel,
 | 
			
		||||
		Channel: channelID,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								vendor/github.com/matterbridge/slack/misc.go → vendor/github.com/nlopes/slack/misc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/matterbridge/slack/misc.go → vendor/github.com/nlopes/slack/misc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,7 @@ import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -42,6 +43,14 @@ func (s WebError) Error() string {
 | 
			
		||||
	return string(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RateLimitedError struct {
 | 
			
		||||
	RetryAfter time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *RateLimitedError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Slack rate limit exceeded, retry after %s", e.RetryAfter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) {
 | 
			
		||||
	body := &bytes.Buffer{}
 | 
			
		||||
	wr := multipart.NewWriter(body)
 | 
			
		||||
@@ -79,12 +88,7 @@ func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error
 | 
			
		||||
		logger.Printf("parseResponseBody: %s\n", string(response))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(response, &intf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return json.Unmarshal(response, &intf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
 | 
			
		||||
@@ -112,8 +116,16 @@ func postWithMultipartResponse(ctx context.Context, path, name, fieldname string
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode == http.StatusTooManyRequests {
 | 
			
		||||
		retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return &RateLimitedError{time.Duration(retry) * time.Second}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		logResponse(resp, debug)
 | 
			
		||||
		return fmt.Errorf("Slack server error: %s.", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
@@ -136,8 +148,16 @@ func postForm(ctx context.Context, endpoint string, values url.Values, intf inte
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode == http.StatusTooManyRequests {
 | 
			
		||||
		retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return &RateLimitedError{time.Duration(retry) * time.Second}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		logResponse(resp, debug)
 | 
			
		||||
		return fmt.Errorf("Slack server error: %s.", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/matterbridge/slack/rtm.go → vendor/github.com/nlopes/slack/rtm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/matterbridge/slack/rtm.go → vendor/github.com/nlopes/slack/rtm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -27,17 +27,8 @@ func (api *Client) StartRTMContext(ctx context.Context) (info *Info, websocketUR
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, "", response.Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// websocket.Dial does not accept url without the port (yet)
 | 
			
		||||
	// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
 | 
			
		||||
	// but slack returns the address with no port, so we have to fix it
 | 
			
		||||
	api.Debugln("Using URL:", response.Info.URL)
 | 
			
		||||
	websocketURL, err = websocketizeURLPort(response.Info.URL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("parsing response URL: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &response.Info, websocketURL, nil
 | 
			
		||||
	return &response.Info, response.Info.URL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectRTM calls the "rtm.connect" endpoint and returns the provided URL and the compact Info block.
 | 
			
		||||
@@ -59,17 +50,8 @@ func (api *Client) ConnectRTMContext(ctx context.Context) (info *Info, websocket
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, "", response.Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// websocket.Dial does not accept url without the port (yet)
 | 
			
		||||
	// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
 | 
			
		||||
	// but slack returns the address with no port, so we have to fix it
 | 
			
		||||
	api.Debugln("Using URL:", response.Info.URL)
 | 
			
		||||
	websocketURL, err = websocketizeURLPort(response.Info.URL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("parsing response URL: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &response.Info, websocketURL, nil
 | 
			
		||||
	return &response.Info, response.Info.URL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRTM returns a RTM, which provides a fully managed connection to
 | 
			
		||||
@@ -90,6 +72,7 @@ func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM {
 | 
			
		||||
		isConnected:      false,
 | 
			
		||||
		wasIntentional:   true,
 | 
			
		||||
		killChannel:      make(chan bool),
 | 
			
		||||
		disconnected:     make(chan struct{}),
 | 
			
		||||
		forcePing:        make(chan bool),
 | 
			
		||||
		rawEvents:        make(chan json.RawMessage),
 | 
			
		||||
		idGen:            NewSafeID(1),
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/matterbridge/slack/slack.go → vendor/github.com/nlopes/slack/slack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/matterbridge/slack/slack.go → vendor/github.com/nlopes/slack/slack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,12 +3,13 @@ package slack
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var logger *log.Logger // A logger that can be set by consumers
 | 
			
		||||
var logger stdLogger // A logger that can be set by consumers
 | 
			
		||||
/*
 | 
			
		||||
  Added as a var so that we can change this for testing purposes
 | 
			
		||||
*/
 | 
			
		||||
@@ -41,12 +42,31 @@ type Client struct {
 | 
			
		||||
	debug bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stdLogger is a logger interface compatible with both stdlib and some
 | 
			
		||||
// 3rd party loggers such as logrus.
 | 
			
		||||
type stdLogger interface {
 | 
			
		||||
	Print(...interface{})
 | 
			
		||||
	Printf(string, ...interface{})
 | 
			
		||||
	Println(...interface{})
 | 
			
		||||
 | 
			
		||||
	Fatal(...interface{})
 | 
			
		||||
	Fatalf(string, ...interface{})
 | 
			
		||||
	Fatalln(...interface{})
 | 
			
		||||
 | 
			
		||||
	Panic(...interface{})
 | 
			
		||||
	Panicf(string, ...interface{})
 | 
			
		||||
	Panicln(...interface{})
 | 
			
		||||
 | 
			
		||||
	Output(int, string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogger let's library users supply a logger, so that api debugging
 | 
			
		||||
// can be logged along with the application's debugging info.
 | 
			
		||||
func SetLogger(l *log.Logger) {
 | 
			
		||||
func SetLogger(l stdLogger) {
 | 
			
		||||
	logger = l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates new Client.
 | 
			
		||||
func New(token string) *Client {
 | 
			
		||||
	s := &Client{}
 | 
			
		||||
	s.config.token = token
 | 
			
		||||
@@ -83,12 +103,12 @@ func (api *Client) SetDebug(debug bool) {
 | 
			
		||||
 | 
			
		||||
func (api *Client) Debugf(format string, v ...interface{}) {
 | 
			
		||||
	if api.debug {
 | 
			
		||||
		logger.Printf(format, v...)
 | 
			
		||||
		logger.Output(2, fmt.Sprintf(format, v...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) Debugln(v ...interface{}) {
 | 
			
		||||
	if api.debug {
 | 
			
		||||
		logger.Println(v...)
 | 
			
		||||
		logger.Output(2, fmt.Sprintln(v...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -200,10 +200,7 @@ func (api *Client) SetUserAsActiveContext(ctx context.Context) error {
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := userRequest(ctx, "users.setActive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPresence changes the currently authenticated user presence
 | 
			
		||||
@@ -247,8 +244,8 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityRes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPhoto changes the currently authenticated user's profile image
 | 
			
		||||
func (api *Client) SetUserPhoto(ctx context.Context, image string, params UserSetPhotoParams) error {
 | 
			
		||||
	return api.SetUserPhoto(context.Background(), image, params)
 | 
			
		||||
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
 | 
			
		||||
	return api.SetUserPhotoContext(context.Background(), image, params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
 | 
			
		||||
@@ -27,6 +27,7 @@ type RTM struct {
 | 
			
		||||
	IncomingEvents   chan RTMEvent
 | 
			
		||||
	outgoingMessages chan OutgoingMessage
 | 
			
		||||
	killChannel      chan bool
 | 
			
		||||
	disconnected     chan struct{} // disconnected is closed when Disconnect is invoked, regardless of connection state. Allows for ManagedConnection to not leak.
 | 
			
		||||
	forcePing        chan bool
 | 
			
		||||
	rawEvents        chan json.RawMessage
 | 
			
		||||
	wasIntentional   bool
 | 
			
		||||
@@ -59,9 +60,14 @@ type RTMOptions struct {
 | 
			
		||||
 | 
			
		||||
// Disconnect and wait, blocking until a successful disconnection.
 | 
			
		||||
func (rtm *RTM) Disconnect() error {
 | 
			
		||||
	// this channel is always closed on disconnect. lets the ManagedConnection() function
 | 
			
		||||
	// properly clean up.
 | 
			
		||||
	close(rtm.disconnected)
 | 
			
		||||
 | 
			
		||||
	if !rtm.isConnected {
 | 
			
		||||
		return errors.New("Invalid call to Disconnect - Slack API is already disconnected")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtm.killChannel <- true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user