forked from lug/matterbridge
		
	Compare commits
	
		
			3 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					bb38a61f3b | ||
| 
						 | 
					c447647af9 | ||
| 
						 | 
					1de64f3f61 | 
							
								
								
									
										28
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -1,36 +1,22 @@
 | 
			
		||||
<!-- This is a bug report template. By following the instructions below and
 | 
			
		||||
filling out the sections with your information, you will help the us to get all
 | 
			
		||||
the necessary data to fix your issue.
 | 
			
		||||
If you have a configuration problem, please first try to create a basic configuration following the instructions on [the wiki](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) before filing an issue.
 | 
			
		||||
 | 
			
		||||
You can also preview your report before submitting it.
 | 
			
		||||
Please answer the following questions. 
 | 
			
		||||
 | 
			
		||||
Text between <!-- and --> marks will be invisible in the report.
 | 
			
		||||
-->
 | 
			
		||||
### Which version of matterbridge are you using?
 | 
			
		||||
run ```matterbridge -version```
 | 
			
		||||
 | 
			
		||||
<!-- If you have a configuration problem, please first try to create a basic configuration following the instructions on [the wiki](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) before filing an issue. -->
 | 
			
		||||
### If you're having problems with mattermost please specify mattermost version. 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Environment
 | 
			
		||||
<!-- run `matterbridge -version` -->
 | 
			
		||||
<!-- If you're having problems with mattermost also specify the mattermost version. -->
 | 
			
		||||
Version:
 | 
			
		||||
 | 
			
		||||
<!-- What operating system are you using ? (be as specific as possible) -->
 | 
			
		||||
Operating system:
 | 
			
		||||
 | 
			
		||||
<!-- If you compiled matterbridge yourself:
 | 
			
		||||
       * Specify the output of `go version` 
 | 
			
		||||
       * Specify the output of `git rev-parse HEAD` -->
 | 
			
		||||
 | 
			
		||||
### Please describe the expected behavior.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Please describe the actual behavior. 
 | 
			
		||||
<!-- Use logs from running `matterbridge -debug` if possible. -->
 | 
			
		||||
#### Use logs from running ```matterbridge -debug``` if possible.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Any steps to reproduce the behavior?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Please add your configuration file 
 | 
			
		||||
<!-- (be sure to exclude or anonymize private data (tokens/passwords)) -->
 | 
			
		||||
#### (be sure to exclude or anonymize private data (tokens/passwords))
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ before_script:
 | 
			
		||||
# flunk the build and immediately stop. It's sorta like having
 | 
			
		||||
# set -e enabled in bash. 
 | 
			
		||||
script:
 | 
			
		||||
 #- test -z $(gofmt -s -l $GO_FILES)  # Fail if a .go file hasn't been formatted with gofmt
 | 
			
		||||
  - test -z $(gofmt -s -l $GO_FILES)  # Fail if a .go file hasn't been formatted with gofmt
 | 
			
		||||
  - go test -v -race $PKGS            # Run all the tests with the race detector enabled
 | 
			
		||||
  - go vet $PKGS                      # go vet is the official Go static analyzer
 | 
			
		||||
  - megacheck $PKGS                   # "go vet on steroids" + linter
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								README.md
									
									
									
									
									
								
							@@ -1,18 +1,17 @@
 | 
			
		||||
# matterbridge
 | 
			
		||||
Click on one of the badges below to join the chat   
 | 
			
		||||
 | 
			
		||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) [](https://inverse.chat) [](https://www.twitch.tv/matterbridge)
 | 
			
		||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) 
 | 
			
		||||
 | 
			
		||||
[](https://github.com/42wim/matterbridge/releases/latest) [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp), Matrix, Steam and ssh-chat
 | 
			
		||||
Has a REST API.  
 | 
			
		||||
Minecraft server chat support via [MatterLink](https://github.com/elytra/MatterLink)
 | 
			
		||||
Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp), Matrix and Steam.
 | 
			
		||||
Has a REST API.
 | 
			
		||||
 | 
			
		||||
# Table of Contents
 | 
			
		||||
 * [Features](https://github.com/42wim/matterbridge/wiki/Features)
 | 
			
		||||
 * [Features](#features)
 | 
			
		||||
 * [Requirements](#requirements)
 | 
			
		||||
 * [Screenshots](https://github.com/42wim/matterbridge/wiki/)
 | 
			
		||||
 * [Installing](#installing)
 | 
			
		||||
@@ -28,21 +27,13 @@ Minecraft server chat support via [MatterLink](https://github.com/elytra/MatterL
 | 
			
		||||
 * [Thanks](#thanks)
 | 
			
		||||
 | 
			
		||||
# Features
 | 
			
		||||
* [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols)
 | 
			
		||||
* [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols)
 | 
			
		||||
* [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes)
 | 
			
		||||
* [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling)
 | 
			
		||||
* [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing)
 | 
			
		||||
* [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups)
 | 
			
		||||
* [API](https://github.com/42wim/matterbridge/wiki/Features#api)
 | 
			
		||||
 | 
			
		||||
## API
 | 
			
		||||
The API is very basic at the moment and rather undocumented.
 | 
			
		||||
 | 
			
		||||
Used by at least 2 projects. Feel free to make a PR to add your project to this list.
 | 
			
		||||
 | 
			
		||||
* [MatterLink](https://github.com/elytra/MatterLink) (Matterbridge link for Minecraft Server chat)
 | 
			
		||||
* [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot)
 | 
			
		||||
* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat (via xmpp), Matrix and Steam. 
 | 
			
		||||
  Pick and mix.
 | 
			
		||||
* Support private groups on your mattermost/slack.
 | 
			
		||||
* Allow for bridging the same bridges, which means you can eg bridge between multiple mattermosts.
 | 
			
		||||
* The bridge is now a gateway which has support multiple in and out bridges. (and supports multiple gateways).
 | 
			
		||||
* Edits and delete messages across bridges that support it (mattermost,slack,discord,gitter,telegram)
 | 
			
		||||
* REST API to read/post messages to bridges (WIP).
 | 
			
		||||
 | 
			
		||||
# Requirements
 | 
			
		||||
Accounts to one of the supported bridges
 | 
			
		||||
@@ -57,15 +48,13 @@ Accounts to one of the supported bridges
 | 
			
		||||
* [Rocket.chat](https://rocket.chat)
 | 
			
		||||
* [Matrix](https://matrix.org)
 | 
			
		||||
* [Steam](https://store.steampowered.com/)
 | 
			
		||||
* [Twitch](https://twitch.tv)
 | 
			
		||||
* [Ssh-chat](https://github.com/shazow/ssh-chat)
 | 
			
		||||
 | 
			
		||||
# Screenshots
 | 
			
		||||
See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
# Installing
 | 
			
		||||
## Binaries
 | 
			
		||||
* Latest stable release [v1.8.0](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Latest stable release [v1.5.0](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)  
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,21 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
	"github.com/labstack/echo/middleware"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"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 {
 | 
			
		||||
@@ -30,30 +30,26 @@ var flog *log.Entry
 | 
			
		||||
var protocol = "api"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Api {
 | 
			
		||||
	b := &Api{BridgeConfig: cfg}
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Api {
 | 
			
		||||
	b := &Api{}
 | 
			
		||||
	e := echo.New()
 | 
			
		||||
	e.HideBanner = true
 | 
			
		||||
	e.HidePort = true
 | 
			
		||||
	b.Messages = ring.Ring{}
 | 
			
		||||
	b.Messages.SetCapacity(b.Config.Buffer)
 | 
			
		||||
	b.Messages.SetCapacity(cfg.Buffer)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	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() {
 | 
			
		||||
		if b.Config.BindAddress == "" {
 | 
			
		||||
			flog.Fatalf("No BindAddress configured.")
 | 
			
		||||
		}
 | 
			
		||||
		flog.Infof("Listening on %s", b.Config.BindAddress)
 | 
			
		||||
		flog.Fatal(e.Start(b.Config.BindAddress))
 | 
			
		||||
		flog.Fatal(e.Start(cfg.BindAddress))
 | 
			
		||||
	}()
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
@@ -82,18 +78,21 @@ func (b *Api) Send(msg config.Message) (string, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Api) handlePostMessage(c echo.Context) error {
 | 
			
		||||
	message := config.Message{}
 | 
			
		||||
	if err := c.Bind(&message); err != nil {
 | 
			
		||||
	message := &ApiMessage{}
 | 
			
		||||
	if err := c.Bind(message); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// these values are fixed
 | 
			
		||||
	message.Channel = "api"
 | 
			
		||||
	message.Protocol = "api"
 | 
			
		||||
	message.Account = b.Account
 | 
			
		||||
	message.ID = ""
 | 
			
		||||
	message.Timestamp = time.Now()
 | 
			
		||||
	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
 | 
			
		||||
	b.Remote <- message
 | 
			
		||||
	b.Remote <- config.Message{
 | 
			
		||||
		Text:     message.Text,
 | 
			
		||||
		Username: message.Username,
 | 
			
		||||
		UserID:   message.UserID,
 | 
			
		||||
		Channel:  "api",
 | 
			
		||||
		Avatar:   message.Avatar,
 | 
			
		||||
		Account:  b.Account,
 | 
			
		||||
		Gateway:  message.Gateway,
 | 
			
		||||
		Protocol: "api",
 | 
			
		||||
	}
 | 
			
		||||
	return c.JSON(http.StatusOK, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -104,24 +103,3 @@ 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,11 +10,10 @@ 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"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
@@ -36,12 +35,6 @@ type Bridge struct {
 | 
			
		||||
	Joined   map[string]bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": "bridge"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge {
 | 
			
		||||
	b := new(Bridge)
 | 
			
		||||
	b.Channels = make(map[string]config.ChannelInfo)
 | 
			
		||||
@@ -52,49 +45,44 @@ 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":
 | 
			
		||||
		bridgeConfig.Config = cfg.Mattermost[name]
 | 
			
		||||
		b.Bridger = bmattermost.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Mattermost[name]
 | 
			
		||||
		b.Bridger = bmattermost.New(cfg.Mattermost[name], bridge.Account, c)
 | 
			
		||||
	case "irc":
 | 
			
		||||
		bridgeConfig.Config = cfg.IRC[name]
 | 
			
		||||
		b.Bridger = birc.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.IRC[name]
 | 
			
		||||
		b.Bridger = birc.New(cfg.IRC[name], bridge.Account, c)
 | 
			
		||||
	case "gitter":
 | 
			
		||||
		bridgeConfig.Config = cfg.Gitter[name]
 | 
			
		||||
		b.Bridger = bgitter.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Gitter[name]
 | 
			
		||||
		b.Bridger = bgitter.New(cfg.Gitter[name], bridge.Account, c)
 | 
			
		||||
	case "slack":
 | 
			
		||||
		bridgeConfig.Config = cfg.Slack[name]
 | 
			
		||||
		b.Bridger = bslack.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Slack[name]
 | 
			
		||||
		b.Bridger = bslack.New(cfg.Slack[name], bridge.Account, c)
 | 
			
		||||
	case "xmpp":
 | 
			
		||||
		bridgeConfig.Config = cfg.Xmpp[name]
 | 
			
		||||
		b.Bridger = bxmpp.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Xmpp[name]
 | 
			
		||||
		b.Bridger = bxmpp.New(cfg.Xmpp[name], bridge.Account, c)
 | 
			
		||||
	case "discord":
 | 
			
		||||
		bridgeConfig.Config = cfg.Discord[name]
 | 
			
		||||
		b.Bridger = bdiscord.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Discord[name]
 | 
			
		||||
		b.Bridger = bdiscord.New(cfg.Discord[name], bridge.Account, c)
 | 
			
		||||
	case "telegram":
 | 
			
		||||
		bridgeConfig.Config = cfg.Telegram[name]
 | 
			
		||||
		b.Bridger = btelegram.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Telegram[name]
 | 
			
		||||
		b.Bridger = btelegram.New(cfg.Telegram[name], bridge.Account, c)
 | 
			
		||||
	case "rocketchat":
 | 
			
		||||
		bridgeConfig.Config = cfg.Rocketchat[name]
 | 
			
		||||
		b.Bridger = brocketchat.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Rocketchat[name]
 | 
			
		||||
		b.Bridger = brocketchat.New(cfg.Rocketchat[name], bridge.Account, c)
 | 
			
		||||
	case "matrix":
 | 
			
		||||
		bridgeConfig.Config = cfg.Matrix[name]
 | 
			
		||||
		b.Bridger = bmatrix.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Matrix[name]
 | 
			
		||||
		b.Bridger = bmatrix.New(cfg.Matrix[name], bridge.Account, c)
 | 
			
		||||
	case "steam":
 | 
			
		||||
		bridgeConfig.Config = cfg.Steam[name]
 | 
			
		||||
		b.Bridger = bsteam.New(bridgeConfig)
 | 
			
		||||
	case "sshchat":
 | 
			
		||||
		bridgeConfig.Config = cfg.Sshchat[name]
 | 
			
		||||
		b.Bridger = bsshchat.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Steam[name]
 | 
			
		||||
		b.Bridger = bsteam.New(cfg.Steam[name], bridge.Account, c)
 | 
			
		||||
	case "api":
 | 
			
		||||
		bridgeConfig.Config = cfg.Api[name]
 | 
			
		||||
		b.Bridger = api.New(bridgeConfig)
 | 
			
		||||
		b.Config = cfg.Api[name]
 | 
			
		||||
		b.Bridger = api.New(cfg.Api[name], bridge.Account, c)
 | 
			
		||||
	}
 | 
			
		||||
	b.Config = bridgeConfig.Config
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +94,7 @@ func (b *Bridge) JoinChannels() error {
 | 
			
		||||
func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map[string]bool) error {
 | 
			
		||||
	for ID, channel := range channels {
 | 
			
		||||
		if !exists[ID] {
 | 
			
		||||
			flog.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
 | 
			
		||||
			log.Infof("%s: joining %s (%s)", b.Account, channel.Name, ID)
 | 
			
		||||
			err := b.JoinChannel(channel)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
 
 | 
			
		||||
@@ -10,14 +10,11 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	EVENT_JOIN_LEAVE        = "join_leave"
 | 
			
		||||
	EVENT_TOPIC_CHANGE      = "topic_change"
 | 
			
		||||
	EVENT_FAILURE           = "failure"
 | 
			
		||||
	EVENT_FILE_FAILURE_SIZE = "file_failure_size"
 | 
			
		||||
	EVENT_AVATAR_DOWNLOAD   = "avatar_download"
 | 
			
		||||
	EVENT_REJOIN_CHANNELS   = "rejoin_channels"
 | 
			
		||||
	EVENT_USER_ACTION       = "user_action"
 | 
			
		||||
	EVENT_MSG_DELETE        = "msg_delete"
 | 
			
		||||
	EVENT_JOIN_LEAVE      = "join_leave"
 | 
			
		||||
	EVENT_FAILURE         = "failure"
 | 
			
		||||
	EVENT_REJOIN_CHANNELS = "rejoin_channels"
 | 
			
		||||
	EVENT_USER_ACTION     = "user_action"
 | 
			
		||||
	EVENT_MSG_DELETE      = "msg_delete"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
@@ -40,9 +37,6 @@ type FileInfo struct {
 | 
			
		||||
	Data    *[]byte
 | 
			
		||||
	Comment string
 | 
			
		||||
	URL     string
 | 
			
		||||
	Size    int64
 | 
			
		||||
	Avatar  bool
 | 
			
		||||
	SHA     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChannelInfo struct {
 | 
			
		||||
@@ -59,16 +53,13 @@ type Protocol struct {
 | 
			
		||||
	BindAddress            string // mattermost, slack // DEPRECATED
 | 
			
		||||
	Buffer                 int    // api
 | 
			
		||||
	Charset                string // irc
 | 
			
		||||
	Debug                  bool   // general
 | 
			
		||||
	EditSuffix             string // mattermost, slack, discord, telegram, gitter
 | 
			
		||||
	EditDisable            bool   // mattermost, slack, discord, telegram, gitter
 | 
			
		||||
	IconURL                string // mattermost, slack
 | 
			
		||||
	IgnoreNicks            string // all protocols
 | 
			
		||||
	IgnoreMessages         string // all protocols
 | 
			
		||||
	Jid                    string // xmpp
 | 
			
		||||
	Label                  string // all protocols
 | 
			
		||||
	Login                  string // mattermost, matrix
 | 
			
		||||
	MediaDownloadSize      int    // all protocols
 | 
			
		||||
	MediaServerDownload    string
 | 
			
		||||
	MediaServerUpload      string
 | 
			
		||||
	MessageDelay           int        // IRC, time in millisecond to wait between messages
 | 
			
		||||
@@ -89,13 +80,11 @@ type Protocol struct {
 | 
			
		||||
	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
 | 
			
		||||
	ShowTopicChange        bool       // slack
 | 
			
		||||
	ShowEmbeds             bool       // discord
 | 
			
		||||
	SkipTLSVerify          bool       // IRC, mattermost
 | 
			
		||||
	StripNick              bool       // all protocols
 | 
			
		||||
@@ -152,19 +141,11 @@ 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 {
 | 
			
		||||
@@ -192,9 +173,6 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,8 @@ package bdiscord
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -13,6 +12,9 @@ import (
 | 
			
		||||
 | 
			
		||||
type bdiscord struct {
 | 
			
		||||
	c              *discordgo.Session
 | 
			
		||||
	Config         *config.Protocol
 | 
			
		||||
	Remote         chan config.Message
 | 
			
		||||
	Account        string
 | 
			
		||||
	Channels       []*discordgo.Channel
 | 
			
		||||
	Nick           string
 | 
			
		||||
	UseChannelID   bool
 | 
			
		||||
@@ -22,18 +24,20 @@ type bdiscord struct {
 | 
			
		||||
	webhookToken   string
 | 
			
		||||
	channelInfoMap map[string]*config.ChannelInfo
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "discord"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *bdiscord {
 | 
			
		||||
	b := &bdiscord{BridgeConfig: cfg}
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *bdiscord {
 | 
			
		||||
	b := &bdiscord{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.userMemberMap = make(map[string]*discordgo.Member)
 | 
			
		||||
	b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
			
		||||
	if b.Config.WebhookURL != "" {
 | 
			
		||||
@@ -140,9 +144,6 @@ func (b *bdiscord) Send(msg config.Message) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if msg.Extra != nil {
 | 
			
		||||
			for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
				b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text)
 | 
			
		||||
			}
 | 
			
		||||
			// check if we have files to upload (from slack, telegram or mattermost)
 | 
			
		||||
			if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
				var err error
 | 
			
		||||
@@ -202,7 +203,6 @@ func (b *bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 | 
			
		||||
	var err error
 | 
			
		||||
	// not relay our own messages
 | 
			
		||||
	if m.Author.Username == b.Nick {
 | 
			
		||||
		return
 | 
			
		||||
@@ -221,13 +221,12 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
 | 
			
		||||
	var text string
 | 
			
		||||
	if m.Content != "" {
 | 
			
		||||
		flog.Debugf("Receiving message %#v", m.Message)
 | 
			
		||||
		if len(m.MentionRoles) > 0 {
 | 
			
		||||
			m.Message.Content = b.replaceRoleMentions(m.Message.Content)
 | 
			
		||||
		}
 | 
			
		||||
		m.Message.Content = b.stripCustomoji(m.Message.Content)
 | 
			
		||||
		m.Message.Content = b.replaceChannelMentions(m.Message.Content)
 | 
			
		||||
		text, err = m.ContentWithMoreMentionsReplaced(b.c)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Errorf("ContentWithMoreMentionsReplaced failed: %s", err)
 | 
			
		||||
			text = m.ContentWithMentionsReplaced()
 | 
			
		||||
		}
 | 
			
		||||
		text = m.ContentWithMentionsReplaced()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg",
 | 
			
		||||
@@ -324,6 +323,18 @@ func (b *bdiscord) getChannelName(id string) string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bdiscord) replaceRoleMentions(text string) string {
 | 
			
		||||
	roles, err := b.c.GuildRoles(b.guildID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Debugf("%#v", string(err.(*discordgo.RESTError).ResponseBody))
 | 
			
		||||
		return text
 | 
			
		||||
	}
 | 
			
		||||
	for _, role := range roles {
 | 
			
		||||
		text = strings.Replace(text, "<@&"+role.ID+">", "@"+role.Name, -1)
 | 
			
		||||
	}
 | 
			
		||||
	return text
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *bdiscord) replaceChannelMentions(text string) string {
 | 
			
		||||
	var err error
 | 
			
		||||
	re := regexp.MustCompile("<#[0-9]+>")
 | 
			
		||||
@@ -359,9 +370,6 @@ func (b *bdiscord) stripCustomoji(text string) string {
 | 
			
		||||
// splitURL splits a webhookURL and returns the id and token
 | 
			
		||||
func (b *bdiscord) splitURL(url string) (string, string) {
 | 
			
		||||
	webhookURLSplit := strings.Split(url, "/")
 | 
			
		||||
	if len(webhookURLSplit) != 7 {
 | 
			
		||||
		log.Fatalf("%s is no correct discord WebhookURL", url)
 | 
			
		||||
	}
 | 
			
		||||
	return webhookURLSplit[len(webhookURLSplit)-2], webhookURLSplit[len(webhookURLSplit)-1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,28 +4,33 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/go-gitter"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bgitter struct {
 | 
			
		||||
	c     *gitter.Gitter
 | 
			
		||||
	User  *gitter.User
 | 
			
		||||
	Users []gitter.User
 | 
			
		||||
	Rooms []gitter.Room
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
	c       *gitter.Gitter
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
	User    *gitter.User
 | 
			
		||||
	Users   []gitter.User
 | 
			
		||||
	Rooms   []gitter.Room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "gitter"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bgitter {
 | 
			
		||||
	return &Bgitter{BridgeConfig: cfg}
 | 
			
		||||
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 (b *Bgitter) Connect() error {
 | 
			
		||||
@@ -122,15 +127,9 @@ func (b *Bgitter) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.c.SendMessage(roomID, rmsg.Username+rmsg.Text)
 | 
			
		||||
		}
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.Comment != "" {
 | 
			
		||||
					msg.Text += fi.Comment + ": "
 | 
			
		||||
				}
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,6 @@ package helper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -20,11 +18,12 @@ func DownloadFile(url string) (*[]byte, error) {
 | 
			
		||||
	}
 | 
			
		||||
	resp, err := client.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.Body.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	io.Copy(&buf, resp.Body)
 | 
			
		||||
	data := buf.Bytes()
 | 
			
		||||
	resp.Body.Close()
 | 
			
		||||
	return &data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -39,25 +38,3 @@ func SplitStringLength(input string, length int) string {
 | 
			
		||||
	}
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handle all the stuff we put into extra
 | 
			
		||||
func HandleExtra(msg *config.Message, general *config.Protocol) []config.Message {
 | 
			
		||||
	extra := msg.Extra
 | 
			
		||||
	rmsg := []config.Message{}
 | 
			
		||||
	if len(extra[config.EVENT_FILE_FAILURE_SIZE]) > 0 {
 | 
			
		||||
		for _, f := range extra[config.EVENT_FILE_FAILURE_SIZE] {
 | 
			
		||||
			fi := f.(config.FileInfo)
 | 
			
		||||
			text := fmt.Sprintf("file %s too big to download (%#v > allowed size: %#v)", fi.Name, fi.Size, general.MediaDownloadSize)
 | 
			
		||||
			rmsg = append(rmsg, config.Message{Text: text, Username: "<system> ", Channel: msg.Channel})
 | 
			
		||||
		}
 | 
			
		||||
		return rmsg
 | 
			
		||||
	}
 | 
			
		||||
	return rmsg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetAvatar(av map[string]string, userid string, general *config.Protocol) string {
 | 
			
		||||
	if sha, ok := av[userid]; ok {
 | 
			
		||||
		return general.MediaServerDownload + "/" + sha + "/" + userid + ".png"
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,11 @@ import (
 | 
			
		||||
	"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"
 | 
			
		||||
	_ "github.com/paulrosania/go-charset/data"
 | 
			
		||||
	"github.com/saintfish/chardet"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
@@ -19,32 +19,34 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
var protocol = "irc"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Birc {
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Birc {
 | 
			
		||||
	b := &Birc{}
 | 
			
		||||
	b.BridgeConfig = cfg
 | 
			
		||||
	b.Config = &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,15 +180,9 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.Local <- rmsg
 | 
			
		||||
		}
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.Comment != "" {
 | 
			
		||||
					msg.Text += fi.Comment + ": "
 | 
			
		||||
				}
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
@@ -201,12 +197,9 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
			
		||||
		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-len(" <message clipped>")]
 | 
			
		||||
			if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
 | 
			
		||||
				text = text[:len(text)-size]
 | 
			
		||||
			}
 | 
			
		||||
			text += " <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 {
 | 
			
		||||
@@ -277,7 +270,6 @@ 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
 | 
			
		||||
	}
 | 
			
		||||
@@ -333,7 +325,7 @@ 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: strings.ToLower(event.Params[0]), Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
			
		||||
	rmsg := config.Message{Username: event.Source.Name, Channel: 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.IsAction() {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,28 +9,33 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	matrix "github.com/matterbridge/gomatrix"
 | 
			
		||||
	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
 | 
			
		||||
var protocol = "matrix"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bmatrix {
 | 
			
		||||
	b := &Bmatrix{BridgeConfig: cfg}
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmatrix {
 | 
			
		||||
	b := &Bmatrix{}
 | 
			
		||||
	b.RoomMap = make(map[string]string)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -75,32 +80,19 @@ func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	channel := b.getRoomID(msg.Channel)
 | 
			
		||||
	// ignore delete messages
 | 
			
		||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
			
		||||
		if msg.ID == "" {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := b.mc.RedactEvent(channel, msg.ID, &matrix.ReqRedact{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return resp.EventID, err
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	channel := b.getRoomID(msg.Channel)
 | 
			
		||||
	flog.Debugf("Sending to channel %s", channel)
 | 
			
		||||
	if msg.Event == config.EVENT_USER_ACTION {
 | 
			
		||||
		resp, err := b.mc.SendMessageEvent(channel, "m.room.message",
 | 
			
		||||
		b.mc.SendMessageEvent(channel, "m.room.message",
 | 
			
		||||
			matrix.TextMessage{"m.emote", msg.Username + msg.Text})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return resp.EventID, err
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.mc.SendText(channel, rmsg.Username+rmsg.Text)
 | 
			
		||||
		}
 | 
			
		||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
@@ -110,17 +102,10 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
				mtype := mime.TypeByExtension("." + sp[len(sp)-1])
 | 
			
		||||
				if strings.Contains(mtype, "image") ||
 | 
			
		||||
					strings.Contains(mtype, "video") {
 | 
			
		||||
					if fi.Comment != "" {
 | 
			
		||||
						_, err := b.mc.SendText(channel, msg.Username+fi.Comment)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							flog.Errorf("file comment failed: %#v", err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					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)
 | 
			
		||||
@@ -143,11 +128,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return resp.EventID, err
 | 
			
		||||
	b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) getRoomID(channel string) string {
 | 
			
		||||
@@ -160,11 +142,58 @@ func (b *Bmatrix) getRoomID(channel string) string {
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
	syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
 | 
			
		||||
	syncer.OnEventType("m.room.redaction", b.handleEvent)
 | 
			
		||||
	syncer.OnEventType("m.room.message", b.handleEvent)
 | 
			
		||||
	syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
 | 
			
		||||
		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()
 | 
			
		||||
			if !ok {
 | 
			
		||||
				flog.Debugf("Unknown room %s", ev.RoomID)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			username := ev.Sender[1:]
 | 
			
		||||
			if b.Config.NoHomeServerSuffix {
 | 
			
		||||
				re := regexp.MustCompile("(.*?):.*")
 | 
			
		||||
				username = re.ReplaceAllString(username, `$1`)
 | 
			
		||||
			}
 | 
			
		||||
			rmsg := config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account, UserID: ev.Sender}
 | 
			
		||||
			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 <= 1000000 {
 | 
			
		||||
					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
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			if err := b.mc.Sync(); err != nil {
 | 
			
		||||
@@ -174,77 +203,3 @@ func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) handleEvent(ev *matrix.Event) {
 | 
			
		||||
	flog.Debugf("Received: %#v", ev)
 | 
			
		||||
	if ev.Sender != b.UserID {
 | 
			
		||||
		b.RLock()
 | 
			
		||||
		channel, ok := b.RoomMap[ev.RoomID]
 | 
			
		||||
		b.RUnlock()
 | 
			
		||||
		if !ok {
 | 
			
		||||
			flog.Debugf("Unknown room %s", ev.RoomID)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		username := ev.Sender[1:]
 | 
			
		||||
		if b.Config.NoHomeServerSuffix {
 | 
			
		||||
			re := regexp.MustCompile("(.*?):.*")
 | 
			
		||||
			username = re.ReplaceAllString(username, `$1`)
 | 
			
		||||
		}
 | 
			
		||||
		var text string
 | 
			
		||||
		text, _ = ev.Content["body"].(string)
 | 
			
		||||
		rmsg := config.Message{Username: username, Text: text, Channel: channel, Account: b.Account, UserID: ev.Sender}
 | 
			
		||||
		rmsg.ID = ev.ID
 | 
			
		||||
		if ev.Type == "m.room.redaction" {
 | 
			
		||||
			rmsg.Event = config.EVENT_MSG_DELETE
 | 
			
		||||
			rmsg.ID = ev.Redacts
 | 
			
		||||
			rmsg.Text = config.EVENT_MSG_DELETE
 | 
			
		||||
			b.Remote <- rmsg
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if ev.Content["msgtype"].(string) == "m.emote" {
 | 
			
		||||
			rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
		}
 | 
			
		||||
		if ev.Content["msgtype"] != nil && 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)
 | 
			
		||||
			// check if we have an image uploaded without extension
 | 
			
		||||
			if !strings.Contains(name, ".") {
 | 
			
		||||
				if ev.Content["msgtype"].(string) == "m.image" {
 | 
			
		||||
					if mtype, ok := ev.Content["mimetype"].(string); ok {
 | 
			
		||||
						mext, _ := mime.ExtensionsByType(mtype)
 | 
			
		||||
						if len(mext) > 0 {
 | 
			
		||||
							name = name + mext[0]
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						// just a default .png extension if we don't have mime info
 | 
			
		||||
						name = name + ".png"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			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})
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				flog.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", name, size, b.General.MediaDownloadSize)
 | 
			
		||||
				rmsg.Event = config.EVENT_FILE_FAILURE_SIZE
 | 
			
		||||
				rmsg.Extra[rmsg.Event] = append(rmsg.Extra[rmsg.Event], config.FileInfo{Name: name, Size: int64(size)})
 | 
			
		||||
			}
 | 
			
		||||
			rmsg.Text = ""
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
 | 
			
		||||
		b.Remote <- rmsg
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,9 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterclient"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -33,20 +32,24 @@ type MMMessage struct {
 | 
			
		||||
type Bmattermost struct {
 | 
			
		||||
	MMhook
 | 
			
		||||
	MMapi
 | 
			
		||||
	TeamId string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
	avatarMap map[string]string
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	TeamId  string
 | 
			
		||||
	Account string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "mattermost"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{BridgeConfig: cfg, avatarMap: make(map[string]string)}
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost {
 | 
			
		||||
	b := &Bmattermost{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.mmMap = make(map[string]string)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
@@ -150,46 +153,17 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
 | 
			
		||||
	message := msg.Text
 | 
			
		||||
	channel := msg.Channel
 | 
			
		||||
 | 
			
		||||
	// map the file SHA to our user (caches the avatar)
 | 
			
		||||
	if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
			
		||||
		fi := msg.Extra["file"][0].(config.FileInfo)
 | 
			
		||||
		/* if we have a sha we have successfully uploaded the file to the media server,
 | 
			
		||||
		so we can now cache the sha */
 | 
			
		||||
		if fi.SHA != "" {
 | 
			
		||||
			flog.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
 | 
			
		||||
			b.avatarMap[msg.UserID] = fi.SHA
 | 
			
		||||
		}
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.Config.PrefixMessagesWithNick {
 | 
			
		||||
		message = nick + message
 | 
			
		||||
	}
 | 
			
		||||
	if b.Config.WebhookURL != "" {
 | 
			
		||||
 | 
			
		||||
		if msg.Extra != nil {
 | 
			
		||||
			for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
				matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: channel, UserName: rmsg.Username,
 | 
			
		||||
					Text: rmsg.Text, Props: make(map[string]interface{})}
 | 
			
		||||
				matterMessage.Props["matterbridge"] = true
 | 
			
		||||
				b.mh.Send(matterMessage)
 | 
			
		||||
			}
 | 
			
		||||
			if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
				for _, f := range msg.Extra["file"] {
 | 
			
		||||
					fi := f.(config.FileInfo)
 | 
			
		||||
					if fi.URL != "" {
 | 
			
		||||
						message += fi.URL
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
 | 
			
		||||
		matterMessage.IconURL = msg.Avatar
 | 
			
		||||
		matterMessage.Channel = channel
 | 
			
		||||
		matterMessage.UserName = nick
 | 
			
		||||
		matterMessage.Type = ""
 | 
			
		||||
		matterMessage.Text = message
 | 
			
		||||
		matterMessage.Text = message
 | 
			
		||||
		matterMessage.Props = make(map[string]interface{})
 | 
			
		||||
		matterMessage.Props["matterbridge"] = true
 | 
			
		||||
		err := b.mh.Send(matterMessage)
 | 
			
		||||
@@ -206,9 +180,6 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
 | 
			
		||||
		return msg.ID, b.mc.DeleteMessage(msg.ID)
 | 
			
		||||
	}
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.mc.PostMessage(b.mc.GetChannelId(channel, ""), rmsg.Username+rmsg.Text)
 | 
			
		||||
		}
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			var err error
 | 
			
		||||
			var res, id string
 | 
			
		||||
@@ -248,8 +219,7 @@ func (b *Bmattermost) handleMatter() {
 | 
			
		||||
		go b.handleMatterClient(mchan)
 | 
			
		||||
	}
 | 
			
		||||
	for message := range mchan {
 | 
			
		||||
		avatar := helper.GetAvatar(b.avatarMap, message.UserID, b.General)
 | 
			
		||||
		rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID, ID: message.ID, Event: message.Event, Extra: message.Extra, Avatar: avatar}
 | 
			
		||||
		rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID, ID: message.ID, Event: message.Event, Extra: message.Extra}
 | 
			
		||||
		text, ok := b.replaceAction(message.Text)
 | 
			
		||||
		if ok {
 | 
			
		||||
			rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
@@ -275,11 +245,6 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// only download avatars if we have a place to upload them (configured mediaserver)
 | 
			
		||||
		if b.General.MediaServerUpload != "" {
 | 
			
		||||
			b.handleDownloadAvatar(message.UserID, message.Channel)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m := &MMMessage{Extra: make(map[string][]interface{})}
 | 
			
		||||
 | 
			
		||||
		props := message.Post.Props
 | 
			
		||||
@@ -316,26 +281,8 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
 | 
			
		||||
				m.Event = config.EVENT_MSG_DELETE
 | 
			
		||||
			}
 | 
			
		||||
			if len(message.Post.FileIds) > 0 {
 | 
			
		||||
				for _, id := range message.Post.FileIds {
 | 
			
		||||
					url, _ := b.mc.Client.GetFileLink(id)
 | 
			
		||||
					finfo, resp := b.mc.Client.GetFileInfo(id)
 | 
			
		||||
					if resp.Error != nil {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					flog.Debugf("trying to download %#v fileid %#v with size %#v", finfo.Name, finfo.Id, finfo.Size)
 | 
			
		||||
					if int(finfo.Size) > b.General.MediaDownloadSize {
 | 
			
		||||
						flog.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", finfo.Name, finfo.Size, b.General.MediaDownloadSize)
 | 
			
		||||
						m.Event = config.EVENT_FILE_FAILURE_SIZE
 | 
			
		||||
						m.Extra[m.Event] = append(m.Extra[m.Event], config.FileInfo{Name: finfo.Name, Comment: message.Text, Size: int64(finfo.Size)})
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					data, resp := b.mc.Client.DownloadFile(id, true)
 | 
			
		||||
					if resp.Error != nil {
 | 
			
		||||
						flog.Errorf("download %s failed %#v", finfo.Name, resp.Error)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					flog.Debugf("download OK %#v %#v", finfo.Name, len(data))
 | 
			
		||||
					m.Extra["file"] = append(m.Extra["file"], config.FileInfo{Name: finfo.Name, Data: &data, URL: url, Comment: message.Text})
 | 
			
		||||
				for _, link := range b.mc.GetFileLinks(message.Post.FileIds) {
 | 
			
		||||
					m.Text = m.Text + "\n" + link
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			mchan <- m
 | 
			
		||||
@@ -364,9 +311,6 @@ func (b *Bmattermost) apiLogin() error {
 | 
			
		||||
 | 
			
		||||
	b.mc = matterclient.New(b.Config.Login, password,
 | 
			
		||||
		b.Config.Team, b.Config.Server)
 | 
			
		||||
	if b.General.Debug {
 | 
			
		||||
		b.mc.SetLogLevel("debug")
 | 
			
		||||
	}
 | 
			
		||||
	b.mc.SkipTLSVerify = b.Config.SkipTLSVerify
 | 
			
		||||
	b.mc.NoTLS = b.Config.NoTLS
 | 
			
		||||
	flog.Infof("Connecting %s (team: %s) on %s", b.Config.Login, b.Config.Team, b.Config.Server)
 | 
			
		||||
@@ -387,27 +331,3 @@ func (b *Bmattermost) replaceAction(text string) (string, bool) {
 | 
			
		||||
	}
 | 
			
		||||
	return text, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleDownloadAvatar downloads the avatar of userid from channel
 | 
			
		||||
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
 | 
			
		||||
// logs an error message if it fails
 | 
			
		||||
func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
 | 
			
		||||
	var name string
 | 
			
		||||
	msg := config.Message{Username: "system", Text: "avatar", Channel: channel, Account: b.Account, UserID: userid, Event: config.EVENT_AVATAR_DOWNLOAD, Extra: make(map[string][]interface{})}
 | 
			
		||||
	if _, ok := b.avatarMap[userid]; !ok {
 | 
			
		||||
		data, resp := b.mc.Client.GetProfileImage(userid, "")
 | 
			
		||||
		if resp.Error != nil {
 | 
			
		||||
			flog.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
 | 
			
		||||
		}
 | 
			
		||||
		if len(data) <= b.General.MediaDownloadSize {
 | 
			
		||||
			name = userid + ".png"
 | 
			
		||||
			flog.Debugf("download OK %#v %#v", name, len(data))
 | 
			
		||||
			msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: name, Data: &data, Avatar: true})
 | 
			
		||||
			flog.Debugf("Sending avatar download message from %#v on %s to gateway", userid, b.Account)
 | 
			
		||||
			flog.Debugf("Message is %#v", msg)
 | 
			
		||||
			b.Remote <- msg
 | 
			
		||||
		} else {
 | 
			
		||||
			flog.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", name, len(data), b.General.MediaDownloadSize)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,9 @@ package brocketchat
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	"github.com/42wim/matterbridge/hook/rockethook"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MMhook struct {
 | 
			
		||||
@@ -15,18 +14,24 @@ type MMhook struct {
 | 
			
		||||
 | 
			
		||||
type Brocketchat struct {
 | 
			
		||||
	MMhook
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "rocketchat"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Brocketchat {
 | 
			
		||||
	return &Brocketchat{BridgeConfig: cfg}
 | 
			
		||||
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 (b *Brocketchat) Command(cmd string) string {
 | 
			
		||||
@@ -58,22 +63,6 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: rmsg.Channel, UserName: rmsg.Username,
 | 
			
		||||
				Text: rmsg.Text}
 | 
			
		||||
			b.mh.Send(matterMessage)
 | 
			
		||||
		}
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text += fi.URL
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
 | 
			
		||||
	matterMessage.Channel = msg.Channel
 | 
			
		||||
	matterMessage.UserName = msg.Username
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,9 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/matterbridge/slack"
 | 
			
		||||
	"html"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -28,23 +27,29 @@ 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
 | 
			
		||||
var protocol = "slack"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bslack {
 | 
			
		||||
	return &Bslack{BridgeConfig: cfg}
 | 
			
		||||
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 (b *Bslack) Command(cmd string) string {
 | 
			
		||||
@@ -108,7 +113,7 @@ func (b *Bslack) Disconnect() error {
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
	// we can only join channels using the API
 | 
			
		||||
	if b.sc != nil {
 | 
			
		||||
	if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" {
 | 
			
		||||
		if strings.HasPrefix(b.Config.Token, "xoxb") {
 | 
			
		||||
			// TODO check if bot has already joined channel
 | 
			
		||||
			return nil
 | 
			
		||||
@@ -135,22 +140,6 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
		message = nick + " " + message
 | 
			
		||||
	}
 | 
			
		||||
	if b.Config.WebhookURL != "" {
 | 
			
		||||
		if msg.Extra != nil {
 | 
			
		||||
			for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
				matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL, Channel: channel, UserName: rmsg.Username,
 | 
			
		||||
					Text: rmsg.Text}
 | 
			
		||||
				b.mh.Send(matterMessage)
 | 
			
		||||
			}
 | 
			
		||||
			if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
				for _, f := range msg.Extra["file"] {
 | 
			
		||||
					fi := f.(config.FileInfo)
 | 
			
		||||
					if fi.URL != "" {
 | 
			
		||||
						message += fi.URL
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
 | 
			
		||||
		matterMessage.Channel = channel
 | 
			
		||||
		matterMessage.UserName = nick
 | 
			
		||||
@@ -172,7 +161,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
 | 
			
		||||
	}
 | 
			
		||||
@@ -200,9 +189,6 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.sc.PostMessage(schannel.ID, rmsg.Username+rmsg.Text, np)
 | 
			
		||||
		}
 | 
			
		||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			var err error
 | 
			
		||||
@@ -304,28 +290,20 @@ func (b *Bslack) handleSlack() {
 | 
			
		||||
			msg.Event = config.EVENT_MSG_DELETE
 | 
			
		||||
			msg.ID = "slack " + message.Raw.DeletedTimestamp
 | 
			
		||||
		}
 | 
			
		||||
		if message.Raw.SubType == "channel_topic" || message.Raw.SubType == "channel_purpose" {
 | 
			
		||||
			msg.Event = config.EVENT_TOPIC_CHANGE
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 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
 | 
			
		||||
			comment := ""
 | 
			
		||||
			results := regexp.MustCompile(`.*?commented: (.*)`).FindAllStringSubmatch(msg.Text, -1)
 | 
			
		||||
			if len(results) > 0 {
 | 
			
		||||
				comment = results[0][1]
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if message.Raw.File.Size > b.General.MediaDownloadSize {
 | 
			
		||||
				flog.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", message.Raw.File.Name, message.Raw.File.Size, b.General.MediaDownloadSize)
 | 
			
		||||
				msg.Event = config.EVENT_FILE_FAILURE_SIZE
 | 
			
		||||
				msg.Extra[msg.Event] = append(msg.Extra[msg.Event], config.FileInfo{Name: message.Raw.File.Name, Comment: comment, Size: int64(message.Raw.File.Size)})
 | 
			
		||||
			} else {
 | 
			
		||||
			if message.Raw.File.Size <= 1000000 {
 | 
			
		||||
				comment := ""
 | 
			
		||||
				data, err := b.downloadFile(message.Raw.File.URLPrivateDownload)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					flog.Errorf("download %s failed %#v", message.Raw.File.URLPrivateDownload, err)
 | 
			
		||||
				} else {
 | 
			
		||||
					results := regexp.MustCompile(`.*?commented: (.*)`).FindAllStringSubmatch(msg.Text, -1)
 | 
			
		||||
					if len(results) > 0 {
 | 
			
		||||
						comment = results[0][1]
 | 
			
		||||
					}
 | 
			
		||||
					msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: message.Raw.File.Name, Data: data, Comment: comment})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -337,14 +315,9 @@ 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:
 | 
			
		||||
			if ev.SubType == "pinned_item" || ev.SubType == "unpinned_item" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			flog.Debugf("Receiving from slackclient %#v", ev)
 | 
			
		||||
			if len(ev.Attachments) > 0 {
 | 
			
		||||
				// skip messages we made ourselves
 | 
			
		||||
				if ev.Attachments[0].CallbackID == "matterbridge" {
 | 
			
		||||
@@ -368,7 +341,7 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			m := &MMMessage{}
 | 
			
		||||
			if ev.BotID == "" && ev.SubType != "message_deleted" && ev.SubType != "file_comment" {
 | 
			
		||||
			if ev.BotID == "" && ev.SubType != "message_deleted" {
 | 
			
		||||
				user, err := b.rtm.GetUserInfo(ev.User)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					continue
 | 
			
		||||
@@ -408,11 +381,6 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
					m.UserID = bot.ID
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ev.SubType == "file_comment" {
 | 
			
		||||
				m.Username = "system"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mchan <- m
 | 
			
		||||
		case *slack.OutgoingErrorEvent:
 | 
			
		||||
			flog.Debugf("%#v", ev.Error())
 | 
			
		||||
@@ -432,8 +400,6 @@ 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:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,139 +0,0 @@
 | 
			
		||||
package bsshchat
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	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{"prefix": 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 {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.w.Write([]byte(rmsg.Username + rmsg.Text + "\r\n"))
 | 
			
		||||
		}
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.Comment != "" {
 | 
			
		||||
					msg.Text += fi.Comment + ": "
 | 
			
		||||
				}
 | 
			
		||||
				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
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -6,7 +6,7 @@ import (
 | 
			
		||||
	"github.com/Philipp15b/go-steam"
 | 
			
		||||
	"github.com/Philipp15b/go-steam/protocol/steamlang"
 | 
			
		||||
	"github.com/Philipp15b/go-steam/steamid"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	//"io/ioutil"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -16,20 +16,25 @@ 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
 | 
			
		||||
var protocol = "steam"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bsteam {
 | 
			
		||||
	b := &Bsteam{BridgeConfig: cfg}
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bsteam {
 | 
			
		||||
	b := &Bsteam{}
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.userMap = make(map[steamid.SteamId]string)
 | 
			
		||||
	b.connected = make(chan struct{})
 | 
			
		||||
	return b
 | 
			
		||||
 
 | 
			
		||||
@@ -7,25 +7,30 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/go-telegram-bot-api/telegram-bot-api"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Btelegram struct {
 | 
			
		||||
	c *tgbotapi.BotAPI
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
	avatarMap map[string]string // keep cache of userid and avatar sha
 | 
			
		||||
	c       *tgbotapi.BotAPI
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "telegram"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Btelegram {
 | 
			
		||||
	return &Btelegram{BridgeConfig: cfg, avatarMap: make(map[string]string)}
 | 
			
		||||
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 (b *Btelegram) Connect() error {
 | 
			
		||||
@@ -36,9 +41,7 @@ func (b *Btelegram) Connect() error {
 | 
			
		||||
		flog.Debugf("%#v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	u := tgbotapi.NewUpdate(0)
 | 
			
		||||
	u.Timeout = 60
 | 
			
		||||
	updates, err := b.c.GetUpdatesChan(u)
 | 
			
		||||
	updates, err := b.c.GetUpdatesChan(tgbotapi.NewUpdate(0))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Debugf("%#v", err)
 | 
			
		||||
		return err
 | 
			
		||||
@@ -64,18 +67,6 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// map the file SHA to our user (caches the avatar)
 | 
			
		||||
	if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
			
		||||
		fi := msg.Extra["file"][0].(config.FileInfo)
 | 
			
		||||
		/* if we have a sha we have successfully uploaded the file to the media server,
 | 
			
		||||
		so we can now cache the sha */
 | 
			
		||||
		if fi.SHA != "" {
 | 
			
		||||
			flog.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
 | 
			
		||||
			b.avatarMap[msg.UserID] = fi.SHA
 | 
			
		||||
		}
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.Config.MessageFormat == "HTML" {
 | 
			
		||||
		msg.Text = makeHTML(msg.Text)
 | 
			
		||||
	}
 | 
			
		||||
@@ -99,14 +90,6 @@ 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" {
 | 
			
		||||
			flog.Debug("Using mode HTML")
 | 
			
		||||
			m.ParseMode = tgbotapi.ModeHTML
 | 
			
		||||
		}
 | 
			
		||||
		if b.Config.MessageFormat == "Markdown" {
 | 
			
		||||
			flog.Debug("Using mode markdown")
 | 
			
		||||
			m.ParseMode = tgbotapi.ModeMarkdown
 | 
			
		||||
		}
 | 
			
		||||
		_, err = b.c.Send(m)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
@@ -115,9 +98,6 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.sendMessage(chatid, rmsg.Username+rmsg.Text)
 | 
			
		||||
		}
 | 
			
		||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			var c tgbotapi.Chattable
 | 
			
		||||
@@ -147,10 +127,6 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
			
		||||
func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
	for update := range updates {
 | 
			
		||||
		flog.Debugf("Receiving from telegram: %#v", update.Message)
 | 
			
		||||
		if update.Message == nil {
 | 
			
		||||
			flog.Error("Getting nil messages, this shouldn't happen.")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var message *tgbotapi.Message
 | 
			
		||||
		username := ""
 | 
			
		||||
		channel := ""
 | 
			
		||||
@@ -186,54 +162,22 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
			}
 | 
			
		||||
			text = message.Text
 | 
			
		||||
			channel = strconv.FormatInt(message.Chat.ID, 10)
 | 
			
		||||
			// only download avatars if we have a place to upload them (configured mediaserver)
 | 
			
		||||
			if b.General.MediaServerUpload != "" {
 | 
			
		||||
				b.handleDownloadAvatar(message.From.ID, channel)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if username == "" {
 | 
			
		||||
			username = "unknown"
 | 
			
		||||
		}
 | 
			
		||||
		if message.Sticker != nil {
 | 
			
		||||
			b.handleDownload(message.Sticker, message.Caption, &fmsg)
 | 
			
		||||
			b.handleDownload(message.Sticker, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Video != nil {
 | 
			
		||||
			b.handleDownload(message.Video, message.Caption, &fmsg)
 | 
			
		||||
			b.handleDownload(message.Video, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Photo != nil {
 | 
			
		||||
			b.handleDownload(message.Photo, message.Caption, &fmsg)
 | 
			
		||||
			b.handleDownload(message.Photo, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Document != nil {
 | 
			
		||||
			b.handleDownload(message.Document, message.Caption, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Voice != nil {
 | 
			
		||||
			b.handleDownload(message.Voice, message.Caption, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Audio != nil {
 | 
			
		||||
			b.handleDownload(message.Audio, message.Caption, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If UseInsecureURL is used we'll have a text in fmsg.Text
 | 
			
		||||
		if fmsg.Text != "" {
 | 
			
		||||
			text = text + fmsg.Text
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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
 | 
			
		||||
			b.handleDownload(message.Document, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// quote the previous message
 | 
			
		||||
@@ -257,9 +201,8 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if text != "" || len(fmsg.Extra) > 0 {
 | 
			
		||||
			avatar := helper.GetAvatar(b.avatarMap, strconv.Itoa(message.From.ID), b.General)
 | 
			
		||||
			flog.Debugf("Sending message from %s on %s to gateway", username, b.Account)
 | 
			
		||||
			msg := config.Message{Username: username, Text: text, Channel: channel, Account: b.Account, UserID: strconv.Itoa(message.From.ID), ID: strconv.Itoa(message.MessageID), Extra: fmsg.Extra, Avatar: avatar}
 | 
			
		||||
			msg := config.Message{Username: username, Text: text, Channel: channel, Account: b.Account, UserID: strconv.Itoa(message.From.ID), ID: strconv.Itoa(message.MessageID), Extra: fmsg.Extra}
 | 
			
		||||
			flog.Debugf("Message is %#v", msg)
 | 
			
		||||
			b.Remote <- msg
 | 
			
		||||
		}
 | 
			
		||||
@@ -274,63 +217,13 @@ func (b *Btelegram) getFileDirectURL(id string) string {
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleDownloadAvatar downloads the avatar of userid from channel
 | 
			
		||||
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
 | 
			
		||||
// logs an error message if it fails
 | 
			
		||||
func (b *Btelegram) handleDownloadAvatar(userid int, channel string) {
 | 
			
		||||
	msg := config.Message{Username: "system", Text: "avatar", Channel: channel, Account: b.Account, UserID: strconv.Itoa(userid), Event: config.EVENT_AVATAR_DOWNLOAD, Extra: make(map[string][]interface{})}
 | 
			
		||||
	if _, ok := b.avatarMap[strconv.Itoa(userid)]; !ok {
 | 
			
		||||
		photos, err := b.c.GetUserProfilePhotos(tgbotapi.UserProfilePhotosConfig{UserID: userid, Limit: 1})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Errorf("Userprofile download failed for %#v %s", userid, err)
 | 
			
		||||
		}
 | 
			
		||||
		if len(photos.Photos) > 0 {
 | 
			
		||||
			photo := photos.Photos[0][0]
 | 
			
		||||
			url := b.getFileDirectURL(photo.FileID)
 | 
			
		||||
			name := strconv.Itoa(userid) + ".png"
 | 
			
		||||
			flog.Debugf("trying to download %#v fileid %#v with size %#v", name, photo.FileID, photo.FileSize)
 | 
			
		||||
			if photo.FileSize <= 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))
 | 
			
		||||
					msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: name, Data: data, Avatar: true})
 | 
			
		||||
					flog.Debugf("Sending avatar download message from %#v on %s to gateway", userid, b.Account)
 | 
			
		||||
					flog.Debugf("Message is %#v", msg)
 | 
			
		||||
					b.Remote <- msg
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				flog.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", name, photo.FileSize, b.General.MediaDownloadSize)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Btelegram) handleDownload(file interface{}, comment string, msg *config.Message) {
 | 
			
		||||
func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	size := 0
 | 
			
		||||
	url := ""
 | 
			
		||||
	name := ""
 | 
			
		||||
	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)
 | 
			
		||||
@@ -363,37 +256,28 @@ func (b *Btelegram) handleDownload(file interface{}, comment string, msg *config
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	}
 | 
			
		||||
	if b.Config.UseInsecureURL {
 | 
			
		||||
		flog.Debugf("Setting message text to :%s", text)
 | 
			
		||||
		msg.Text = text
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// 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 <= b.General.MediaDownloadSize {
 | 
			
		||||
	if size <= 1000000 {
 | 
			
		||||
		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))
 | 
			
		||||
			msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: name, Data: data, Comment: comment})
 | 
			
		||||
			msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: name, Data: data})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		flog.Errorf("File %#v to large to download (%#v). MediaDownloadSize is %#v", name, size, b.General.MediaDownloadSize)
 | 
			
		||||
		msg.Event = config.EVENT_FILE_FAILURE_SIZE
 | 
			
		||||
		msg.Extra[msg.Event] = append(msg.Extra[msg.Event], config.FileInfo{Name: name, Comment: comment, Size: int64(size)})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Btelegram) sendMessage(chatid int64, text string) (string, error) {
 | 
			
		||||
	m := tgbotapi.NewMessage(chatid, text)
 | 
			
		||||
	if b.Config.MessageFormat == "HTML" {
 | 
			
		||||
		flog.Debug("Using mode HTML")
 | 
			
		||||
		m.ParseMode = tgbotapi.ModeHTML
 | 
			
		||||
	}
 | 
			
		||||
	if b.Config.MessageFormat == "Markdown" {
 | 
			
		||||
		flog.Debug("Using mode markdown")
 | 
			
		||||
		m.ParseMode = tgbotapi.ModeMarkdown
 | 
			
		||||
	}
 | 
			
		||||
	res, err := b.c.Send(m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,7 @@ package bxmpp
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/jpillora/backoff"
 | 
			
		||||
	"github.com/mattn/go-xmpp"
 | 
			
		||||
 | 
			
		||||
@@ -15,19 +14,24 @@ import (
 | 
			
		||||
type Bxmpp struct {
 | 
			
		||||
	xc      *xmpp.Client
 | 
			
		||||
	xmppMap map[string]string
 | 
			
		||||
	*config.BridgeConfig
 | 
			
		||||
	Config  *config.Protocol
 | 
			
		||||
	Remote  chan config.Message
 | 
			
		||||
	Account string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
var protocol = "xmpp"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": protocol})
 | 
			
		||||
	flog = log.WithFields(log.Fields{"module": protocol})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *config.BridgeConfig) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{BridgeConfig: cfg}
 | 
			
		||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bxmpp {
 | 
			
		||||
	b := &Bxmpp{}
 | 
			
		||||
	b.xmppMap = make(map[string]string)
 | 
			
		||||
	b.Config = &cfg
 | 
			
		||||
	b.Account = account
 | 
			
		||||
	b.Remote = c
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -82,17 +86,11 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
			b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.Config.Muc, Text: rmsg.Username + rmsg.Text})
 | 
			
		||||
		}
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.Comment != "" {
 | 
			
		||||
					msg.Text += fi.Comment + ": "
 | 
			
		||||
				}
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text += fi.URL
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
			
		||||
			}
 | 
			
		||||
@@ -117,7 +115,7 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
 | 
			
		||||
		TLSConfig: tc,
 | 
			
		||||
 | 
			
		||||
		//StartTLS:      false,
 | 
			
		||||
		Debug:                        b.General.Debug,
 | 
			
		||||
		Debug:                        true,
 | 
			
		||||
		Session:                      true,
 | 
			
		||||
		Status:                       "",
 | 
			
		||||
		StatusMessage:                "",
 | 
			
		||||
@@ -173,7 +171,7 @@ func (b *Bxmpp) handleXmpp() error {
 | 
			
		||||
				if len(s) == 2 {
 | 
			
		||||
					nick = s[1]
 | 
			
		||||
				}
 | 
			
		||||
				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" && !strings.Contains(v.Text, "</subject>") {
 | 
			
		||||
				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" {
 | 
			
		||||
					rmsg := config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote}
 | 
			
		||||
					rmsg.Text, ok = b.replaceAction(rmsg.Text)
 | 
			
		||||
					if ok {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,77 +1,3 @@
 | 
			
		||||
# v1.8.0
 | 
			
		||||
## New features
 | 
			
		||||
* general: Send chat notification if media is too big to be re-uploaded to MediaServer. See #359
 | 
			
		||||
* general: Download (and upload) avatar images from mattermost and telegram when mediaserver is configured. Closes #362
 | 
			
		||||
* general: Add label support in RemoteNickFormat
 | 
			
		||||
* general: Prettier info/debug log output
 | 
			
		||||
* mattermost: Download files and reupload to supported bridges (mattermost). Closes #357
 | 
			
		||||
* slack: Add ShowTopicChange option. Allow/disable topic change messages (currently only from slack). Closes #353
 | 
			
		||||
* slack: Add support for file comments (slack). Closes #346
 | 
			
		||||
* telegram: Add comment to file upload from telegram. Show comments on all bridges. Closes #358
 | 
			
		||||
* telegram: Add markdown support (telegram). #355
 | 
			
		||||
* api: Give api access to whole config.Message (and events). Closes #374
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* discord: Check for a valid WebhookURL (discord). Closes #367
 | 
			
		||||
* discord: Fix role mention replace issues
 | 
			
		||||
* irc: Truncate messages sent to IRC based on byte count (#368)
 | 
			
		||||
* mattermost: Add file download urls also to mattermost webhooks #356
 | 
			
		||||
* telegram: Fix panic on nil messages (telegram). Closes #366
 | 
			
		||||
* telegram: Fix the UseInsecureURL text (telegram). Closes #184
 | 
			
		||||
 | 
			
		||||
# v1.7.1
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: Enable Long Polling for Telegram. Reduces bandwidth consumption. (#350)
 | 
			
		||||
 | 
			
		||||
# v1.7.0
 | 
			
		||||
## New features
 | 
			
		||||
* matrix: Add support for deleting messages from/to matrix (matrix). Closes #320
 | 
			
		||||
* xmpp: Ignore <subject> messages (xmpp). #272
 | 
			
		||||
* irc: Add twitch support (irc) to README / wiki
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* general: Change RemoteNickFormat replacement order. Closes #336
 | 
			
		||||
* general: Make edits/delete work for bridges that gets reused. Closes #342
 | 
			
		||||
* general: Lowercase irc channels in config. Closes #348
 | 
			
		||||
* matrix: Fix possible panics (matrix). Closes #333
 | 
			
		||||
* matrix: Add an extension to images without one (matrix). #331
 | 
			
		||||
* api: Obey the Gateway value from the json (api). Closes #344
 | 
			
		||||
* xmpp: Print only debug messages when specified (xmpp). Closes #345
 | 
			
		||||
* xmpp: Allow xmpp to receive the extra messages (file uploads) when text is empty. #295
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
FROM cmosh/alpine-arm:edge
 | 
			
		||||
ENTRYPOINT ["/bin/matterbridge"]
 | 
			
		||||
 | 
			
		||||
COPY . /go/src/github.com/42wim/matterbridge
 | 
			
		||||
RUN apk update && apk add go git gcc musl-dev ca-certificates \
 | 
			
		||||
        && cd /go/src/github.com/42wim/matterbridge \
 | 
			
		||||
        && export GOPATH=/go \
 | 
			
		||||
        && go get \
 | 
			
		||||
        && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
 | 
			
		||||
        && rm -rf /go \
 | 
			
		||||
        && apk del --purge git go gcc musl-dev
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	//	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"github.com/hashicorp/golang-lru"
 | 
			
		||||
@@ -29,15 +29,8 @@ type Gateway struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BrMsgID struct {
 | 
			
		||||
	br        *bridge.Bridge
 | 
			
		||||
	ID        string
 | 
			
		||||
	ChannelID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flog *log.Entry
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flog = log.WithFields(log.Fields{"prefix": "gateway"})
 | 
			
		||||
	br *bridge.Bridge
 | 
			
		||||
	ID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Gateway, r *Router) *Gateway {
 | 
			
		||||
@@ -84,10 +77,10 @@ func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
 | 
			
		||||
	br.Disconnect()
 | 
			
		||||
	time.Sleep(time.Second * 5)
 | 
			
		||||
RECONNECT:
 | 
			
		||||
	flog.Infof("Reconnecting %s", br.Account)
 | 
			
		||||
	log.Infof("Reconnecting %s", br.Account)
 | 
			
		||||
	err := br.Connect()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
 | 
			
		||||
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
 | 
			
		||||
		time.Sleep(time.Second * 60)
 | 
			
		||||
		goto RECONNECT
 | 
			
		||||
	}
 | 
			
		||||
@@ -100,10 +93,6 @@ func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
 | 
			
		||||
		if isApi(br.Account) {
 | 
			
		||||
			br.Channel = "api"
 | 
			
		||||
		}
 | 
			
		||||
		// make sure to lowercase irc channels in config #348
 | 
			
		||||
		if strings.HasPrefix(br.Account, "irc.") {
 | 
			
		||||
			br.Channel = strings.ToLower(br.Channel)
 | 
			
		||||
		}
 | 
			
		||||
		ID := br.Channel + br.Account
 | 
			
		||||
		if _, ok := gw.Channels[ID]; !ok {
 | 
			
		||||
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
 | 
			
		||||
@@ -129,12 +118,6 @@ func (gw *Gateway) mapChannels() error {
 | 
			
		||||
 | 
			
		||||
func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
 | 
			
		||||
	var channels []config.ChannelInfo
 | 
			
		||||
 | 
			
		||||
	// for messages received from the api check that the gateway is the specified one
 | 
			
		||||
	if msg.Protocol == "api" && gw.Name != msg.Gateway {
 | 
			
		||||
		return channels
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if source channel is in only, do nothing
 | 
			
		||||
	for _, channel := range gw.Channels {
 | 
			
		||||
		// lookup the channel from the message
 | 
			
		||||
@@ -151,7 +134,7 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// do samechannelgateway flogic
 | 
			
		||||
		// do samechannelgateway logic
 | 
			
		||||
		if channel.SameChannel[msg.Gateway] {
 | 
			
		||||
			if msg.Channel == channel.Name && msg.Account != dest.Account {
 | 
			
		||||
				channels = append(channels, *channel)
 | 
			
		||||
@@ -176,51 +159,30 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
			dest.Protocol != "slack" &&
 | 
			
		||||
			dest.Protocol != "mattermost" &&
 | 
			
		||||
			dest.Protocol != "telegram" &&
 | 
			
		||||
			dest.Protocol != "matrix" &&
 | 
			
		||||
			dest.Protocol != "xmpp" &&
 | 
			
		||||
			len(msg.Extra[config.EVENT_FILE_FAILURE_SIZE]) == 0 {
 | 
			
		||||
			dest.Protocol != "matrix" {
 | 
			
		||||
			if msg.Text == "" {
 | 
			
		||||
				return brMsgIDs
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Avatar downloads are only relevant for telegram and mattermost for now
 | 
			
		||||
	if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
			
		||||
		if dest.Protocol != "mattermost" &&
 | 
			
		||||
			dest.Protocol != "telegram" {
 | 
			
		||||
			return brMsgIDs
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// only relay join/part when configged
 | 
			
		||||
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
 | 
			
		||||
		return brMsgIDs
 | 
			
		||||
	}
 | 
			
		||||
	if msg.Event == config.EVENT_TOPIC_CHANGE && !gw.Bridges[dest.Account].Config.ShowTopicChange {
 | 
			
		||||
		return brMsgIDs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// broadcast to every out channel (irc QUIT)
 | 
			
		||||
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
 | 
			
		||||
		flog.Debug("empty channel")
 | 
			
		||||
		log.Debug("empty channel")
 | 
			
		||||
		return brMsgIDs
 | 
			
		||||
	}
 | 
			
		||||
	originchannel := msg.Channel
 | 
			
		||||
	origmsg := msg
 | 
			
		||||
	channels := gw.getDestChannel(&msg, *dest)
 | 
			
		||||
	for _, channel := range channels {
 | 
			
		||||
		// Only send the avatar download event to ourselves.
 | 
			
		||||
		if msg.Event == config.EVENT_AVATAR_DOWNLOAD {
 | 
			
		||||
			if channel.ID != getChannelID(origmsg) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// do not send to ourself for any other event
 | 
			
		||||
			if channel.ID == getChannelID(origmsg) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		// do not send to ourself
 | 
			
		||||
		if channel.ID == getChannelID(origmsg) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
			
		||||
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
 | 
			
		||||
		msg.Channel = channel.Name
 | 
			
		||||
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
 | 
			
		||||
		msg.Username = gw.modifyUsername(origmsg, dest)
 | 
			
		||||
@@ -228,9 +190,7 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
 | 
			
		||||
			IDs := res.([]*BrMsgID)
 | 
			
		||||
			for _, id := range IDs {
 | 
			
		||||
				// check protocol, bridge name and channelname
 | 
			
		||||
				// for people that reuse the same bridge multiple times. see #342
 | 
			
		||||
				if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
 | 
			
		||||
				if dest.Protocol == id.br.Protocol {
 | 
			
		||||
					msg.ID = id.ID
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -245,7 +205,7 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
		}
 | 
			
		||||
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
 | 
			
		||||
		if mID != "" {
 | 
			
		||||
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
 | 
			
		||||
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return brMsgIDs
 | 
			
		||||
@@ -258,18 +218,15 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
 | 
			
		||||
	}
 | 
			
		||||
	if msg.Text == "" {
 | 
			
		||||
		// we have an attachment or actual bytes
 | 
			
		||||
		if msg.Extra != nil &&
 | 
			
		||||
			(msg.Extra["attachments"] != nil ||
 | 
			
		||||
				len(msg.Extra["file"]) > 0 ||
 | 
			
		||||
				len(msg.Extra[config.EVENT_FILE_FAILURE_SIZE]) > 0) {
 | 
			
		||||
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
 | 
			
		||||
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
 | 
			
		||||
		if msg.Username == entry {
 | 
			
		||||
			flog.Debugf("ignoring %s from %s", msg.Username, msg.Account)
 | 
			
		||||
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -278,11 +235,11 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
 | 
			
		||||
		if entry != "" {
 | 
			
		||||
			re, err := regexp.Compile(entry)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				flog.Errorf("incorrect regexp %s for %s", entry, msg.Account)
 | 
			
		||||
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if re.MatchString(msg.Text) {
 | 
			
		||||
				flog.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
 | 
			
		||||
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -309,7 +266,7 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
 | 
			
		||||
		// TODO move compile to bridge init somewhere
 | 
			
		||||
		re, err := regexp.Compile(search)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		msg.Username = re.ReplaceAllString(msg.Username, replace)
 | 
			
		||||
@@ -327,10 +284,9 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
 | 
			
		||||
		}
 | 
			
		||||
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
 | 
			
		||||
	}
 | 
			
		||||
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{LABEL}", br.Config.Label, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
			
		||||
	return nick
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -357,16 +313,12 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
 | 
			
		||||
		// TODO move compile to bridge init somewhere
 | 
			
		||||
		re, err := regexp.Compile(search)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			flog.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		msg.Text = re.ReplaceAllString(msg.Text, replace)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// messages from api have Gateway specified, don't overwrite
 | 
			
		||||
	if msg.Protocol != "api" {
 | 
			
		||||
		msg.Gateway = gw.Name
 | 
			
		||||
	}
 | 
			
		||||
	msg.Gateway = gw.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
			
		||||
@@ -385,17 +337,14 @@ func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
			
		||||
			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 {
 | 
			
		||||
				flog.Errorf("mediaserver upload failed: %#v", err)
 | 
			
		||||
				continue
 | 
			
		||||
				log.Errorf("mediaserver upload failed: %#v", err)
 | 
			
		||||
			}
 | 
			
		||||
			flog.Debugf("mediaserver download URL = %s", durl)
 | 
			
		||||
			// we uploaded the file successfully. Add the SHA
 | 
			
		||||
			extra.SHA = sha1sum
 | 
			
		||||
			msg.Extra["file"][i] = extra
 | 
			
		||||
			log.Debugf("mediaserver download URL = %s", durl)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/gateway/samechannel"
 | 
			
		||||
	//log "github.com/sirupsen/logrus"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	//	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -42,13 +42,12 @@ func NewRouter(cfg *config.Config) (*Router, error) {
 | 
			
		||||
func (r *Router) Start() error {
 | 
			
		||||
	m := make(map[string]*bridge.Bridge)
 | 
			
		||||
	for _, gw := range r.Gateways {
 | 
			
		||||
		flog.Infof("Parsing gateway %s", gw.Name)
 | 
			
		||||
		for _, br := range gw.Bridges {
 | 
			
		||||
			m[br.Account] = br
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, br := range m {
 | 
			
		||||
		flog.Infof("Starting bridge: %s ", br.Account)
 | 
			
		||||
		log.Infof("Starting bridge: %s ", br.Account)
 | 
			
		||||
		err := br.Connect()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,21 +5,22 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/gateway"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/google/gops/agent"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	prefixed "github.com/x-cray/logrus-prefixed-formatter"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.8.0"
 | 
			
		||||
	version = "1.5.1"
 | 
			
		||||
	githash string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: true})
 | 
			
		||||
	flog := log.WithFields(log.Fields{"prefix": "main"})
 | 
			
		||||
	flagConfig := flag.String("conf", "matterbridge.toml", "config file")
 | 
			
		||||
	flagDebug := flag.Bool("debug", false, "enable debug")
 | 
			
		||||
	flagVersion := flag.Bool("version", false, "show version")
 | 
			
		||||
@@ -34,24 +35,22 @@ func main() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if *flagDebug || os.Getenv("DEBUG") == "1" {
 | 
			
		||||
		log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: false})
 | 
			
		||||
		flog.Info("Enabling debug")
 | 
			
		||||
		log.Info("Enabling debug")
 | 
			
		||||
		log.SetLevel(log.DebugLevel)
 | 
			
		||||
	}
 | 
			
		||||
	flog.Printf("Running version %s %s", version, githash)
 | 
			
		||||
	log.Printf("Running version %s %s", version, githash)
 | 
			
		||||
	if strings.Contains(version, "-dev") {
 | 
			
		||||
		flog.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
 | 
			
		||||
		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
 | 
			
		||||
	}
 | 
			
		||||
	cfg := config.NewConfig(*flagConfig)
 | 
			
		||||
	cfg.General.Debug = *flagDebug
 | 
			
		||||
	r, err := gateway.NewRouter(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Fatalf("Starting gateway failed: %s", err)
 | 
			
		||||
		log.Fatalf("Starting gateway failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = r.Start()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		flog.Fatalf("Starting gateway failed: %s", err)
 | 
			
		||||
		log.Fatalf("Starting gateway failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	flog.Printf("Gateway(s) started succesfully. Now relaying messages")
 | 
			
		||||
	log.Printf("Gateway(s) started succesfully. Now relaying messages")
 | 
			
		||||
	select {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -85,10 +85,6 @@ MessageLength=400
 | 
			
		||||
#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
 | 
			
		||||
@@ -117,21 +113,16 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#The string "{NOPINGNICK}" (case sensitive) will be replaced by the actual nick / username, but with a ZWSP inside the nick, so the irc user with the same nick won't get pinged. See https://github.com/42wim/matterbridge/issues/175 for more information
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -140,11 +131,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#XMPP section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -207,20 +193,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -229,11 +210,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#hipchat section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -288,20 +264,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -310,11 +281,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#mattermost section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -429,20 +395,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -451,11 +412,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#Gitter section
 | 
			
		||||
#Best to make a dedicated gitter account for the bot.
 | 
			
		||||
@@ -500,20 +456,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -522,11 +473,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#slack section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -562,7 +508,6 @@ WebhookBindAddress="0.0.0.0:9999"
 | 
			
		||||
#Icon that will be showed in slack
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
IconURL="https://robohash.org/{NICK}.png?size=48x48"
 | 
			
		||||
@@ -619,20 +564,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -641,11 +581,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#discord section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -714,20 +649,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -736,11 +666,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#telegram section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -808,20 +733,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -830,11 +750,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#rocketchat section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -903,20 +818,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -925,11 +835,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#matrix section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -990,20 +895,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -1012,11 +912,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#steam section
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -1071,20 +966,15 @@ ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
 | 
			
		||||
#Enable to show users joins/parts from other bridges 
 | 
			
		||||
#Currently works for messages from the following bridges: irc, mattermost, slack
 | 
			
		||||
#Only works hiding/show messages from irc and mattermost bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowJoinPart=false
 | 
			
		||||
 | 
			
		||||
@@ -1093,11 +983,6 @@ ShowJoinPart=false
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
#Enable to show topic changes from other bridges 
 | 
			
		||||
#Only works hiding/show topic changes from slack bridge for now
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
ShowTopicChange=false
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#API
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -1119,14 +1004,9 @@ Buffer=1000
 | 
			
		||||
#OPTIONAL (no authorization if token is empty)
 | 
			
		||||
Token="mytoken"
 | 
			
		||||
 | 
			
		||||
#extra label that can be used in the RemoteNickFormat
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
Label=""
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="{NICK}"
 | 
			
		||||
@@ -1141,7 +1021,6 @@ RemoteNickFormat="{NICK}"
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
 | 
			
		||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
@@ -1165,15 +1044,6 @@ 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, matrix and mattermost
 | 
			
		||||
#
 | 
			
		||||
#Optional (default 1000000 (1 megabyte))
 | 
			
		||||
MediaDownloadSize=1000000
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#Gateway configuration
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,7 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	prefixed "github.com/x-cray/logrus-prefixed-formatter"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/hashicorp/golang-lru"
 | 
			
		||||
@@ -74,16 +73,12 @@ type MMClient struct {
 | 
			
		||||
func New(login, pass, team, server string) *MMClient {
 | 
			
		||||
	cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
 | 
			
		||||
	mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100), Users: make(map[string]*model.User)}
 | 
			
		||||
	log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true})
 | 
			
		||||
	mmclient.log = log.WithFields(log.Fields{"prefix": "matterclient"})
 | 
			
		||||
	mmclient.log = log.WithFields(log.Fields{"module": "matterclient"})
 | 
			
		||||
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
 | 
			
		||||
	mmclient.lruCache, _ = lru.New(500)
 | 
			
		||||
	return mmclient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) SetDebugLog() {
 | 
			
		||||
	log.SetFormatter(&prefixed.TextFormatter{PrefixPadding: 13, DisableColors: true, FullTimestamp: false})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) SetLogLevel(level string) {
 | 
			
		||||
	l, err := log.ParseLevel(level)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -590,9 +585,9 @@ func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
 | 
			
		||||
func (m *MMClient) UpdateLastViewed(channelId string) {
 | 
			
		||||
	m.log.Debugf("posting lastview %#v", channelId)
 | 
			
		||||
	view := &model.ChannelView{ChannelId: channelId}
 | 
			
		||||
	_, resp := m.Client.ViewChannel(m.User.Id, view)
 | 
			
		||||
	if resp.Error != nil {
 | 
			
		||||
		m.log.Errorf("ChannelView update for %s failed: %s", channelId, resp.Error)
 | 
			
		||||
	res, _ := m.Client.ViewChannel(m.User.Id, view)
 | 
			
		||||
	if !res {
 | 
			
		||||
		m.log.Errorf("ChannelView update for %s failed", channelId)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/sirupsen/logrus/doc.go → vendor/github.com/Sirupsen/logrus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/sirupsen/logrus/doc.go → vendor/github.com/Sirupsen/logrus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,7 +7,7 @@ The simplest way to use Logrus is simply the package-level exported logger:
 | 
			
		||||
  package main
 | 
			
		||||
 | 
			
		||||
  import (
 | 
			
		||||
    log "github.com/sirupsen/logrus"
 | 
			
		||||
    log "github.com/Sirupsen/logrus"
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  func main() {
 | 
			
		||||
@@ -21,6 +21,6 @@ The simplest way to use Logrus is simply the package-level exported logger:
 | 
			
		||||
Output:
 | 
			
		||||
  time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
 | 
			
		||||
 | 
			
		||||
For a full guide visit https://github.com/sirupsen/logrus
 | 
			
		||||
For a full guide visit https://github.com/Sirupsen/logrus
 | 
			
		||||
*/
 | 
			
		||||
package logrus
 | 
			
		||||
							
								
								
									
										85
									
								
								vendor/github.com/sirupsen/logrus/entry.go → vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										85
									
								
								vendor/github.com/sirupsen/logrus/entry.go → vendor/github.com/Sirupsen/logrus/entry.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -35,7 +35,6 @@ type Entry struct {
 | 
			
		||||
	Time time.Time
 | 
			
		||||
 | 
			
		||||
	// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
 | 
			
		||||
	// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
 | 
			
		||||
	Level Level
 | 
			
		||||
 | 
			
		||||
	// Message passed to Debug, Info, Warn, Error, Fatal or Panic
 | 
			
		||||
@@ -94,16 +93,29 @@ func (entry Entry) log(level Level, msg string) {
 | 
			
		||||
	entry.Level = level
 | 
			
		||||
	entry.Message = msg
 | 
			
		||||
 | 
			
		||||
	entry.fireHooks()
 | 
			
		||||
 | 
			
		||||
	if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
 | 
			
		||||
		entry.Logger.mu.Lock()
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
 | 
			
		||||
		entry.Logger.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
	buffer = bufferPool.Get().(*bytes.Buffer)
 | 
			
		||||
	buffer.Reset()
 | 
			
		||||
	defer bufferPool.Put(buffer)
 | 
			
		||||
	entry.Buffer = buffer
 | 
			
		||||
 | 
			
		||||
	entry.write()
 | 
			
		||||
 | 
			
		||||
	serialized, err := entry.Logger.Formatter.Format(&entry)
 | 
			
		||||
	entry.Buffer = nil
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		entry.Logger.mu.Lock()
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
 | 
			
		||||
		entry.Logger.mu.Unlock()
 | 
			
		||||
	} else {
 | 
			
		||||
		entry.Logger.mu.Lock()
 | 
			
		||||
		_, err = entry.Logger.Out.Write(serialized)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
 | 
			
		||||
		}
 | 
			
		||||
		entry.Logger.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// To avoid Entry#log() returning a value that only would make sense for
 | 
			
		||||
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
 | 
			
		||||
@@ -113,33 +125,8 @@ func (entry Entry) log(level Level, msg string) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is not declared with a pointer value because otherwise
 | 
			
		||||
// race conditions will occur when using multiple goroutines
 | 
			
		||||
func (entry Entry) fireHooks() {
 | 
			
		||||
	entry.Logger.mu.Lock()
 | 
			
		||||
	defer entry.Logger.mu.Unlock()
 | 
			
		||||
	err := entry.Logger.Hooks.Fire(entry.Level, &entry)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) write() {
 | 
			
		||||
	serialized, err := entry.Logger.Formatter.Format(entry)
 | 
			
		||||
	entry.Logger.mu.Lock()
 | 
			
		||||
	defer entry.Logger.mu.Unlock()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
 | 
			
		||||
	} else {
 | 
			
		||||
		_, err = entry.Logger.Out.Write(serialized)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Debug(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= DebugLevel {
 | 
			
		||||
	if entry.Logger.Level >= DebugLevel {
 | 
			
		||||
		entry.log(DebugLevel, fmt.Sprint(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -149,13 +136,13 @@ func (entry *Entry) Print(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Info(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= InfoLevel {
 | 
			
		||||
	if entry.Logger.Level >= InfoLevel {
 | 
			
		||||
		entry.log(InfoLevel, fmt.Sprint(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Warn(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= WarnLevel {
 | 
			
		||||
	if entry.Logger.Level >= WarnLevel {
 | 
			
		||||
		entry.log(WarnLevel, fmt.Sprint(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -165,20 +152,20 @@ func (entry *Entry) Warning(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Error(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= ErrorLevel {
 | 
			
		||||
	if entry.Logger.Level >= ErrorLevel {
 | 
			
		||||
		entry.log(ErrorLevel, fmt.Sprint(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Fatal(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= FatalLevel {
 | 
			
		||||
	if entry.Logger.Level >= FatalLevel {
 | 
			
		||||
		entry.log(FatalLevel, fmt.Sprint(args...))
 | 
			
		||||
	}
 | 
			
		||||
	Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Panic(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= PanicLevel {
 | 
			
		||||
	if entry.Logger.Level >= PanicLevel {
 | 
			
		||||
		entry.log(PanicLevel, fmt.Sprint(args...))
 | 
			
		||||
	}
 | 
			
		||||
	panic(fmt.Sprint(args...))
 | 
			
		||||
@@ -187,13 +174,13 @@ func (entry *Entry) Panic(args ...interface{}) {
 | 
			
		||||
// Entry Printf family functions
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= DebugLevel {
 | 
			
		||||
	if entry.Logger.Level >= DebugLevel {
 | 
			
		||||
		entry.Debug(fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Infof(format string, args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= InfoLevel {
 | 
			
		||||
	if entry.Logger.Level >= InfoLevel {
 | 
			
		||||
		entry.Info(fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -203,7 +190,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= WarnLevel {
 | 
			
		||||
	if entry.Logger.Level >= WarnLevel {
 | 
			
		||||
		entry.Warn(fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -213,20 +200,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= ErrorLevel {
 | 
			
		||||
	if entry.Logger.Level >= ErrorLevel {
 | 
			
		||||
		entry.Error(fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= FatalLevel {
 | 
			
		||||
	if entry.Logger.Level >= FatalLevel {
 | 
			
		||||
		entry.Fatal(fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
	Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= PanicLevel {
 | 
			
		||||
	if entry.Logger.Level >= PanicLevel {
 | 
			
		||||
		entry.Panic(fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -234,13 +221,13 @@ func (entry *Entry) Panicf(format string, args ...interface{}) {
 | 
			
		||||
// Entry Println family functions
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Debugln(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= DebugLevel {
 | 
			
		||||
	if entry.Logger.Level >= DebugLevel {
 | 
			
		||||
		entry.Debug(entry.sprintlnn(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Infoln(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= InfoLevel {
 | 
			
		||||
	if entry.Logger.Level >= InfoLevel {
 | 
			
		||||
		entry.Info(entry.sprintlnn(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -250,7 +237,7 @@ func (entry *Entry) Println(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Warnln(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= WarnLevel {
 | 
			
		||||
	if entry.Logger.Level >= WarnLevel {
 | 
			
		||||
		entry.Warn(entry.sprintlnn(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -260,20 +247,20 @@ func (entry *Entry) Warningln(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Errorln(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= ErrorLevel {
 | 
			
		||||
	if entry.Logger.Level >= ErrorLevel {
 | 
			
		||||
		entry.Error(entry.sprintlnn(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Fatalln(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= FatalLevel {
 | 
			
		||||
	if entry.Logger.Level >= FatalLevel {
 | 
			
		||||
		entry.Fatal(entry.sprintlnn(args...))
 | 
			
		||||
	}
 | 
			
		||||
	Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (entry *Entry) Panicln(args ...interface{}) {
 | 
			
		||||
	if entry.Logger.level() >= PanicLevel {
 | 
			
		||||
	if entry.Logger.Level >= PanicLevel {
 | 
			
		||||
		entry.Panic(entry.sprintlnn(args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +1,23 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	prefixed "github.com/x-cray/logrus-prefixed-formatter"
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	// "os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var log = logrus.New()
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	formatter := new(prefixed.TextFormatter)
 | 
			
		||||
	log.Formatter = formatter
 | 
			
		||||
	log.Formatter = new(logrus.JSONFormatter)
 | 
			
		||||
	log.Formatter = new(logrus.TextFormatter) // default
 | 
			
		||||
 | 
			
		||||
	// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
 | 
			
		||||
	// if err == nil {
 | 
			
		||||
	// 	log.Out = file
 | 
			
		||||
	// } else {
 | 
			
		||||
	// 	log.Info("Failed to log to file, using default stderr")
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	log.Level = logrus.DebugLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -17,42 +25,34 @@ func main() {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := recover()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Fatal message
 | 
			
		||||
			log.WithFields(logrus.Fields{
 | 
			
		||||
				"omg":    true,
 | 
			
		||||
				"err":    err,
 | 
			
		||||
				"number": 100,
 | 
			
		||||
			}).Fatal("[main] The ice breaks!")
 | 
			
		||||
			}).Fatal("The ice breaks!")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// You could either provide a map key called `prefix` to add prefix
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"prefix": "main",
 | 
			
		||||
		"animal": "walrus",
 | 
			
		||||
		"number": 8,
 | 
			
		||||
	}).Debug("Started observing beach")
 | 
			
		||||
 | 
			
		||||
	// Or you can simply add prefix in square brackets within message itself
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"animal": "walrus",
 | 
			
		||||
		"size":   10,
 | 
			
		||||
	}).Debug("[main] A group of walrus emerges from the ocean")
 | 
			
		||||
	}).Info("A group of walrus emerges from the ocean")
 | 
			
		||||
 | 
			
		||||
	// Warning message
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"omg":    true,
 | 
			
		||||
		"number": 122,
 | 
			
		||||
	}).Warn("[main] The group's number increased tremendously!")
 | 
			
		||||
	}).Warn("The group's number increased tremendously!")
 | 
			
		||||
 | 
			
		||||
	// Information message
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"prefix":      "sensor",
 | 
			
		||||
		"temperature": -4,
 | 
			
		||||
	}).Info("Temperature changes")
 | 
			
		||||
	}).Debug("Temperature changes")
 | 
			
		||||
 | 
			
		||||
	// Panic message
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"prefix": "sensor",
 | 
			
		||||
		"animal": "orca",
 | 
			
		||||
		"size":   9009,
 | 
			
		||||
	}).Panic("It's over 9000!")
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var log = logrus.New()
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log.Formatter = new(logrus.TextFormatter) // default
 | 
			
		||||
	log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"animal": "walrus",
 | 
			
		||||
		"size":   10,
 | 
			
		||||
	}).Info("A group of walrus emerges from the ocean")
 | 
			
		||||
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"omg":    true,
 | 
			
		||||
		"number": 122,
 | 
			
		||||
	}).Warn("The group's number increased tremendously!")
 | 
			
		||||
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"omg":    true,
 | 
			
		||||
		"number": 100,
 | 
			
		||||
	}).Fatal("The ice breaks!")
 | 
			
		||||
}
 | 
			
		||||
@@ -31,14 +31,14 @@ func SetFormatter(formatter Formatter) {
 | 
			
		||||
func SetLevel(level Level) {
 | 
			
		||||
	std.mu.Lock()
 | 
			
		||||
	defer std.mu.Unlock()
 | 
			
		||||
	std.SetLevel(level)
 | 
			
		||||
	std.Level = level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLevel returns the standard logger level.
 | 
			
		||||
func GetLevel() Level {
 | 
			
		||||
	std.mu.Lock()
 | 
			
		||||
	defer std.mu.Unlock()
 | 
			
		||||
	return std.level()
 | 
			
		||||
	return std.Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddHook adds a hook to the standard logger hooks.
 | 
			
		||||
@@ -2,7 +2,7 @@ package logrus
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
const defaultTimestampFormat = time.RFC3339
 | 
			
		||||
const DefaultTimestampFormat = time.RFC3339
 | 
			
		||||
 | 
			
		||||
// The Formatter interface is used to implement a custom Formatter. It takes an
 | 
			
		||||
// `Entry`. It exposes all the fields, including the default ones:
 | 
			
		||||
@@ -1,13 +1,12 @@
 | 
			
		||||
// +build !windows,!nacl,!plan9
 | 
			
		||||
 | 
			
		||||
package syslog
 | 
			
		||||
package logrus_syslog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"log/syslog"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SyslogHook to send logs via syslog.
 | 
			
		||||
							
								
								
									
										67
									
								
								vendor/github.com/Sirupsen/logrus/hooks/test/test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/Sirupsen/logrus/hooks/test/test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
package test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// test.Hook is a hook designed for dealing with logs in test scenarios.
 | 
			
		||||
type Hook struct {
 | 
			
		||||
	Entries []*logrus.Entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Installs a test hook for the global logger.
 | 
			
		||||
func NewGlobal() *Hook {
 | 
			
		||||
 | 
			
		||||
	hook := new(Hook)
 | 
			
		||||
	logrus.AddHook(hook)
 | 
			
		||||
 | 
			
		||||
	return hook
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Installs a test hook for a given local logger.
 | 
			
		||||
func NewLocal(logger *logrus.Logger) *Hook {
 | 
			
		||||
 | 
			
		||||
	hook := new(Hook)
 | 
			
		||||
	logger.Hooks.Add(hook)
 | 
			
		||||
 | 
			
		||||
	return hook
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Creates a discarding logger and installs the test hook.
 | 
			
		||||
func NewNullLogger() (*logrus.Logger, *Hook) {
 | 
			
		||||
 | 
			
		||||
	logger := logrus.New()
 | 
			
		||||
	logger.Out = ioutil.Discard
 | 
			
		||||
 | 
			
		||||
	return logger, NewLocal(logger)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Hook) Fire(e *logrus.Entry) error {
 | 
			
		||||
	t.Entries = append(t.Entries, e)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Hook) Levels() []logrus.Level {
 | 
			
		||||
	return logrus.AllLevels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LastEntry returns the last entry that was logged or nil.
 | 
			
		||||
func (t *Hook) LastEntry() (l *logrus.Entry) {
 | 
			
		||||
 | 
			
		||||
	if i := len(t.Entries) - 1; i < 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return t.Entries[i]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset removes all Entries from this test hook.
 | 
			
		||||
func (t *Hook) Reset() {
 | 
			
		||||
	t.Entries = make([]*logrus.Entry, 0)
 | 
			
		||||
}
 | 
			
		||||
@@ -6,11 +6,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type fieldKey string
 | 
			
		||||
 | 
			
		||||
// FieldMap allows customization of the key names for default fields.
 | 
			
		||||
type FieldMap map[fieldKey]string
 | 
			
		||||
 | 
			
		||||
// Default key names for the default fields
 | 
			
		||||
const (
 | 
			
		||||
	FieldKeyMsg   = "msg"
 | 
			
		||||
	FieldKeyLevel = "level"
 | 
			
		||||
@@ -25,7 +22,6 @@ func (f FieldMap) resolve(key fieldKey) string {
 | 
			
		||||
	return string(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JSONFormatter formats logs into parsable json
 | 
			
		||||
type JSONFormatter struct {
 | 
			
		||||
	// TimestampFormat sets the format used for marshaling timestamps.
 | 
			
		||||
	TimestampFormat string
 | 
			
		||||
@@ -33,26 +29,25 @@ type JSONFormatter struct {
 | 
			
		||||
	// DisableTimestamp allows disabling automatic timestamps in output
 | 
			
		||||
	DisableTimestamp bool
 | 
			
		||||
 | 
			
		||||
	// FieldMap allows users to customize the names of keys for default fields.
 | 
			
		||||
	// FieldMap allows users to customize the names of keys for various fields.
 | 
			
		||||
	// As an example:
 | 
			
		||||
	// formatter := &JSONFormatter{
 | 
			
		||||
	//   	FieldMap: FieldMap{
 | 
			
		||||
	// 		 FieldKeyTime: "@timestamp",
 | 
			
		||||
	// 		 FieldKeyLevel: "@level",
 | 
			
		||||
	// 		 FieldKeyMsg: "@message",
 | 
			
		||||
	// 		 FieldKeyLevel: "@message",
 | 
			
		||||
	//    },
 | 
			
		||||
	// }
 | 
			
		||||
	FieldMap FieldMap
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format renders a single log entry
 | 
			
		||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 | 
			
		||||
	data := make(Fields, len(entry.Data)+3)
 | 
			
		||||
	for k, v := range entry.Data {
 | 
			
		||||
		switch v := v.(type) {
 | 
			
		||||
		case error:
 | 
			
		||||
			// Otherwise errors are ignored by `encoding/json`
 | 
			
		||||
			// https://github.com/sirupsen/logrus/issues/137
 | 
			
		||||
			// https://github.com/Sirupsen/logrus/issues/137
 | 
			
		||||
			data[k] = v.Error()
 | 
			
		||||
		default:
 | 
			
		||||
			data[k] = v
 | 
			
		||||
@@ -62,7 +57,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 | 
			
		||||
 | 
			
		||||
	timestampFormat := f.TimestampFormat
 | 
			
		||||
	if timestampFormat == "" {
 | 
			
		||||
		timestampFormat = defaultTimestampFormat
 | 
			
		||||
		timestampFormat = DefaultTimestampFormat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !f.DisableTimestamp {
 | 
			
		||||
@@ -4,7 +4,6 @@ import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Logger struct {
 | 
			
		||||
@@ -25,7 +24,7 @@ type Logger struct {
 | 
			
		||||
	Formatter Formatter
 | 
			
		||||
	// The logging level the logger should log at. This is typically (and defaults
 | 
			
		||||
	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
 | 
			
		||||
	// logged.
 | 
			
		||||
	// logged. `logrus.Debug` is useful in
 | 
			
		||||
	Level Level
 | 
			
		||||
	// Used to sync writing to the log. Locking is enabled by Default
 | 
			
		||||
	mu MutexWrap
 | 
			
		||||
@@ -113,7 +112,7 @@ func (logger *Logger) WithError(err error) *Entry {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= DebugLevel {
 | 
			
		||||
	if logger.Level >= DebugLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Debugf(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -121,7 +120,7 @@ func (logger *Logger) Debugf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Infof(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= InfoLevel {
 | 
			
		||||
	if logger.Level >= InfoLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Infof(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -135,7 +134,7 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= WarnLevel {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Warnf(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -143,7 +142,7 @@ func (logger *Logger) Warnf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= WarnLevel {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Warnf(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -151,7 +150,7 @@ func (logger *Logger) Warningf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= ErrorLevel {
 | 
			
		||||
	if logger.Level >= ErrorLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Errorf(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -159,7 +158,7 @@ func (logger *Logger) Errorf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= FatalLevel {
 | 
			
		||||
	if logger.Level >= FatalLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Fatalf(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -168,7 +167,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.level() >= PanicLevel {
 | 
			
		||||
	if logger.Level >= PanicLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Panicf(format, args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -176,7 +175,7 @@ func (logger *Logger) Panicf(format string, args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Debug(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= DebugLevel {
 | 
			
		||||
	if logger.Level >= DebugLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Debug(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -184,7 +183,7 @@ func (logger *Logger) Debug(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Info(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= InfoLevel {
 | 
			
		||||
	if logger.Level >= InfoLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Info(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -198,7 +197,7 @@ func (logger *Logger) Print(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warn(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= WarnLevel {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Warn(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -206,7 +205,7 @@ func (logger *Logger) Warn(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warning(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= WarnLevel {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Warn(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -214,7 +213,7 @@ func (logger *Logger) Warning(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Error(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= ErrorLevel {
 | 
			
		||||
	if logger.Level >= ErrorLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Error(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -222,7 +221,7 @@ func (logger *Logger) Error(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Fatal(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= FatalLevel {
 | 
			
		||||
	if logger.Level >= FatalLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Fatal(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -231,7 +230,7 @@ func (logger *Logger) Fatal(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Panic(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= PanicLevel {
 | 
			
		||||
	if logger.Level >= PanicLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Panic(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -239,7 +238,7 @@ func (logger *Logger) Panic(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Debugln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= DebugLevel {
 | 
			
		||||
	if logger.Level >= DebugLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Debugln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -247,7 +246,7 @@ func (logger *Logger) Debugln(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Infoln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= InfoLevel {
 | 
			
		||||
	if logger.Level >= InfoLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Infoln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -261,7 +260,7 @@ func (logger *Logger) Println(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warnln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= WarnLevel {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Warnln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -269,7 +268,7 @@ func (logger *Logger) Warnln(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warningln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= WarnLevel {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Warnln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -277,7 +276,7 @@ func (logger *Logger) Warningln(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Errorln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= ErrorLevel {
 | 
			
		||||
	if logger.Level >= ErrorLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Errorln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -285,7 +284,7 @@ func (logger *Logger) Errorln(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Fatalln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= FatalLevel {
 | 
			
		||||
	if logger.Level >= FatalLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Fatalln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -294,7 +293,7 @@ func (logger *Logger) Fatalln(args ...interface{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Panicln(args ...interface{}) {
 | 
			
		||||
	if logger.level() >= PanicLevel {
 | 
			
		||||
	if logger.Level >= PanicLevel {
 | 
			
		||||
		entry := logger.newEntry()
 | 
			
		||||
		entry.Panicln(args...)
 | 
			
		||||
		logger.releaseEntry(entry)
 | 
			
		||||
@@ -307,17 +306,3 @@ func (logger *Logger) Panicln(args ...interface{}) {
 | 
			
		||||
func (logger *Logger) SetNoLock() {
 | 
			
		||||
	logger.mu.Disable()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) level() Level {
 | 
			
		||||
	return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) SetLevel(level Level) {
 | 
			
		||||
	atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) AddHook(hook Hook) {
 | 
			
		||||
	logger.mu.Lock()
 | 
			
		||||
	defer logger.mu.Unlock()
 | 
			
		||||
	logger.Hooks.Add(hook)
 | 
			
		||||
}
 | 
			
		||||
@@ -10,7 +10,7 @@ import (
 | 
			
		||||
type Fields map[string]interface{}
 | 
			
		||||
 | 
			
		||||
// Level type
 | 
			
		||||
type Level uint32
 | 
			
		||||
type Level uint8
 | 
			
		||||
 | 
			
		||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
 | 
			
		||||
func (level Level) String() string {
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_appengine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_appengine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
// +build appengine
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(f io.Writer) bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/Sirupsen/logrus/terminal_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
// +build darwin freebsd openbsd netbsd dragonfly
 | 
			
		||||
// +build !appengine
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
const ioctlReadTermios = syscall.TIOCGETA
 | 
			
		||||
 | 
			
		||||
type Termios syscall.Termios
 | 
			
		||||
@@ -7,8 +7,8 @@
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
const ioctlReadTermios = unix.TCGETS
 | 
			
		||||
const ioctlReadTermios = syscall.TCGETS
 | 
			
		||||
 | 
			
		||||
type Termios unix.Termios
 | 
			
		||||
type Termios syscall.Termios
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
// Based on ssh/terminal:
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux darwin freebsd openbsd netbsd dragonfly
 | 
			
		||||
// +build !appengine
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(f io.Writer) bool {
 | 
			
		||||
	var termios Termios
 | 
			
		||||
	switch v := f.(type) {
 | 
			
		||||
	case *os.File:
 | 
			
		||||
		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
 | 
			
		||||
		return err == 0
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/Sirupsen/logrus/terminal_solaris.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
// +build solaris,!appengine
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(f io.Writer) bool {
 | 
			
		||||
	switch v := f.(type) {
 | 
			
		||||
	case *os.File:
 | 
			
		||||
		_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
 | 
			
		||||
		return err == nil
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/Sirupsen/logrus/terminal_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
// Based on ssh/terminal:
 | 
			
		||||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build windows,!appengine
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
 | 
			
		||||
func IsTerminal(f io.Writer) bool {
 | 
			
		||||
	switch v := f.(type) {
 | 
			
		||||
	case *os.File:
 | 
			
		||||
		var st uint32
 | 
			
		||||
		r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
 | 
			
		||||
		return r != 0 && e == 0
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -14,7 +14,7 @@ const (
 | 
			
		||||
	red     = 31
 | 
			
		||||
	green   = 32
 | 
			
		||||
	yellow  = 33
 | 
			
		||||
	blue    = 36
 | 
			
		||||
	blue    = 34
 | 
			
		||||
	gray    = 37
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -26,7 +26,6 @@ func init() {
 | 
			
		||||
	baseTimestamp = time.Now()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TextFormatter formats logs into text
 | 
			
		||||
type TextFormatter struct {
 | 
			
		||||
	// Set to true to bypass checking for a TTY before outputting colors.
 | 
			
		||||
	ForceColors bool
 | 
			
		||||
@@ -53,6 +52,10 @@ type TextFormatter struct {
 | 
			
		||||
	// QuoteEmptyFields will wrap empty fields in quotes if true
 | 
			
		||||
	QuoteEmptyFields bool
 | 
			
		||||
 | 
			
		||||
	// QuoteCharacter can be set to the override the default quoting character "
 | 
			
		||||
	// with something else. For example: ', or `.
 | 
			
		||||
	QuoteCharacter string
 | 
			
		||||
 | 
			
		||||
	// Whether the logger's out is to a terminal
 | 
			
		||||
	isTerminal bool
 | 
			
		||||
 | 
			
		||||
@@ -60,12 +63,14 @@ type TextFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *TextFormatter) init(entry *Entry) {
 | 
			
		||||
	if len(f.QuoteCharacter) == 0 {
 | 
			
		||||
		f.QuoteCharacter = "\""
 | 
			
		||||
	}
 | 
			
		||||
	if entry.Logger != nil {
 | 
			
		||||
		f.isTerminal = checkIfTerminal(entry.Logger.Out)
 | 
			
		||||
		f.isTerminal = IsTerminal(entry.Logger.Out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format renders a single log entry
 | 
			
		||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 | 
			
		||||
	var b *bytes.Buffer
 | 
			
		||||
	keys := make([]string, 0, len(entry.Data))
 | 
			
		||||
@@ -90,7 +95,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 | 
			
		||||
 | 
			
		||||
	timestampFormat := f.TimestampFormat
 | 
			
		||||
	if timestampFormat == "" {
 | 
			
		||||
		timestampFormat = defaultTimestampFormat
 | 
			
		||||
		timestampFormat = DefaultTimestampFormat
 | 
			
		||||
	}
 | 
			
		||||
	if isColored {
 | 
			
		||||
		f.printColored(b, entry, keys, timestampFormat)
 | 
			
		||||
@@ -148,7 +153,7 @@ func (f *TextFormatter) needsQuoting(text string) bool {
 | 
			
		||||
		if !((ch >= 'a' && ch <= 'z') ||
 | 
			
		||||
			(ch >= 'A' && ch <= 'Z') ||
 | 
			
		||||
			(ch >= '0' && ch <= '9') ||
 | 
			
		||||
			ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
 | 
			
		||||
			ch == '-' || ch == '.') {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -156,23 +161,29 @@ func (f *TextFormatter) needsQuoting(text string) bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
 | 
			
		||||
	if b.Len() > 0 {
 | 
			
		||||
		b.WriteByte(' ')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.WriteString(key)
 | 
			
		||||
	b.WriteByte('=')
 | 
			
		||||
	f.appendValue(b, value)
 | 
			
		||||
	b.WriteByte(' ')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
 | 
			
		||||
	stringVal, ok := value.(string)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		stringVal = fmt.Sprint(value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !f.needsQuoting(stringVal) {
 | 
			
		||||
		b.WriteString(stringVal)
 | 
			
		||||
	} else {
 | 
			
		||||
		b.WriteString(fmt.Sprintf("%q", stringVal))
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		if !f.needsQuoting(value) {
 | 
			
		||||
			b.WriteString(value)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
 | 
			
		||||
		}
 | 
			
		||||
	case error:
 | 
			
		||||
		errmsg := value.Error()
 | 
			
		||||
		if !f.needsQuoting(errmsg) {
 | 
			
		||||
			b.WriteString(errmsg)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		fmt.Fprint(b, value)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/bwmarrin/discordgo/discord.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/bwmarrin/discordgo/discord.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
 | 
			
		||||
const VERSION = "0.18.0"
 | 
			
		||||
const VERSION = "0.17.0"
 | 
			
		||||
 | 
			
		||||
// ErrMFA will be risen by New when the user has 2FA.
 | 
			
		||||
var ErrMFA = errors.New("account has 2FA enabled")
 | 
			
		||||
@@ -50,7 +50,7 @@ func New(args ...interface{}) (s *Session, err error) {
 | 
			
		||||
	// Create an empty Session interface.
 | 
			
		||||
	s = &Session{
 | 
			
		||||
		State:                  NewState(),
 | 
			
		||||
		Ratelimiter:            NewRatelimiter(),
 | 
			
		||||
		ratelimiter:            NewRatelimiter(),
 | 
			
		||||
		StateEnabled:           true,
 | 
			
		||||
		Compress:               true,
 | 
			
		||||
		ShouldReconnectOnError: true,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/bwmarrin/discordgo/endpoints.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/bwmarrin/discordgo/endpoints.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -71,6 +71,7 @@ var (
 | 
			
		||||
	EndpointUserNotes          = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
 | 
			
		||||
 | 
			
		||||
	EndpointGuild                = func(gID string) string { return EndpointGuilds + gID }
 | 
			
		||||
	EndpointGuildInivtes         = func(gID string) string { return EndpointGuilds + gID + "/invites" }
 | 
			
		||||
	EndpointGuildChannels        = func(gID string) string { return EndpointGuilds + gID + "/channels" }
 | 
			
		||||
	EndpointGuildMembers         = func(gID string) string { return EndpointGuilds + gID + "/members" }
 | 
			
		||||
	EndpointGuildMember          = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
 | 
			
		||||
@@ -97,7 +98,7 @@ var (
 | 
			
		||||
	EndpointChannelMessages           = func(cID string) string { return EndpointChannels + cID + "/messages" }
 | 
			
		||||
	EndpointChannelMessage            = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
 | 
			
		||||
	EndpointChannelMessageAck         = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
 | 
			
		||||
	EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
 | 
			
		||||
	EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" }
 | 
			
		||||
	EndpointChannelMessagesPins       = func(cID string) string { return EndpointChannel(cID) + "/pins" }
 | 
			
		||||
	EndpointChannelMessagePin         = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
 | 
			
		||||
 | 
			
		||||
@@ -121,8 +122,6 @@ var (
 | 
			
		||||
	EndpointRelationship        = func(uID string) string { return EndpointRelationships() + "/" + uID }
 | 
			
		||||
	EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
 | 
			
		||||
 | 
			
		||||
	EndpointGuildCreate = EndpointAPI + "guilds"
 | 
			
		||||
 | 
			
		||||
	EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
 | 
			
		||||
 | 
			
		||||
	EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@ type EventHandler interface {
 | 
			
		||||
	Type() string
 | 
			
		||||
 | 
			
		||||
	// Handle is called whenever an event of Type() happens.
 | 
			
		||||
	// It is the receivers responsibility to type assert that the interface
 | 
			
		||||
	// It is the recievers responsibility to type assert that the interface
 | 
			
		||||
	// is the expected struct.
 | 
			
		||||
	Handle(*Session, interface{})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -79,7 +79,7 @@ func main() {
 | 
			
		||||
	ap.Name = Name
 | 
			
		||||
	ap, err = dg.ApplicationCreate(ap)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error creating new application,", err)
 | 
			
		||||
		fmt.Println("error creating new applicaiton,", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/bwmarrin/discordgo/logging.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/bwmarrin/discordgo/logging.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ const (
 | 
			
		||||
	LogError int = iota
 | 
			
		||||
 | 
			
		||||
	// LogWarning level is used for very abnormal events and errors that are
 | 
			
		||||
	// also returned to a calling function.
 | 
			
		||||
	// also returend to a calling function.
 | 
			
		||||
	LogWarning
 | 
			
		||||
 | 
			
		||||
	// LogInformational level is used for normal non-error activity
 | 
			
		||||
@@ -34,34 +34,26 @@ const (
 | 
			
		||||
	LogDebug
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Logger can be used to replace the standard logging for discordgo
 | 
			
		||||
var Logger func(msgL, caller int, format string, a ...interface{})
 | 
			
		||||
 | 
			
		||||
// msglog provides package wide logging consistancy for discordgo
 | 
			
		||||
// the format, a...  portion this command follows that of fmt.Printf
 | 
			
		||||
//   msgL   : LogLevel of the message
 | 
			
		||||
//   caller : 1 + the number of callers away from the message source
 | 
			
		||||
//   format : Printf style message format
 | 
			
		||||
//   a ...  : comma separated list of values to pass
 | 
			
		||||
//   a ...  : comma seperated list of values to pass
 | 
			
		||||
func msglog(msgL, caller int, format string, a ...interface{}) {
 | 
			
		||||
 | 
			
		||||
	if Logger != nil {
 | 
			
		||||
		Logger(msgL, caller, format, a...)
 | 
			
		||||
	} else {
 | 
			
		||||
	pc, file, line, _ := runtime.Caller(caller)
 | 
			
		||||
 | 
			
		||||
		pc, file, line, _ := runtime.Caller(caller)
 | 
			
		||||
	files := strings.Split(file, "/")
 | 
			
		||||
	file = files[len(files)-1]
 | 
			
		||||
 | 
			
		||||
		files := strings.Split(file, "/")
 | 
			
		||||
		file = files[len(files)-1]
 | 
			
		||||
	name := runtime.FuncForPC(pc).Name()
 | 
			
		||||
	fns := strings.Split(name, ".")
 | 
			
		||||
	name = fns[len(fns)-1]
 | 
			
		||||
 | 
			
		||||
		name := runtime.FuncForPC(pc).Name()
 | 
			
		||||
		fns := strings.Split(name, ".")
 | 
			
		||||
		name = fns[len(fns)-1]
 | 
			
		||||
	msg := fmt.Sprintf(format, a...)
 | 
			
		||||
 | 
			
		||||
		msg := fmt.Sprintf(format, a...)
 | 
			
		||||
 | 
			
		||||
		log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
 | 
			
		||||
	}
 | 
			
		||||
	log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// helper function that wraps msglog for the Session struct
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/bwmarrin/discordgo/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -237,7 +237,7 @@ func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, e
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1)
 | 
			
		||||
		content = strings.Replace(content, "<&"+role.ID+">", "@"+role.Name, -1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								vendor/github.com/bwmarrin/discordgo/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/bwmarrin/discordgo/ratelimit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -41,8 +41,8 @@ func NewRatelimiter() *RateLimiter {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetBucket retrieves or creates a bucket
 | 
			
		||||
func (r *RateLimiter) GetBucket(key string) *Bucket {
 | 
			
		||||
// getBucket retrieves or creates a bucket
 | 
			
		||||
func (r *RateLimiter) getBucket(key string) *Bucket {
 | 
			
		||||
	r.Lock()
 | 
			
		||||
	defer r.Unlock()
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +51,7 @@ func (r *RateLimiter) GetBucket(key string) *Bucket {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b := &Bucket{
 | 
			
		||||
		Remaining: 1,
 | 
			
		||||
		remaining: 1,
 | 
			
		||||
		Key:       key,
 | 
			
		||||
		global:    r.global,
 | 
			
		||||
	}
 | 
			
		||||
@@ -68,37 +68,27 @@ func (r *RateLimiter) GetBucket(key string) *Bucket {
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetWaitTime returns the duration you should wait for a Bucket
 | 
			
		||||
func (r *RateLimiter) GetWaitTime(b *Bucket, minRemaining int) time.Duration {
 | 
			
		||||
// LockBucket Locks until a request can be made
 | 
			
		||||
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
 | 
			
		||||
 | 
			
		||||
	b := r.getBucket(bucketID)
 | 
			
		||||
 | 
			
		||||
	b.Lock()
 | 
			
		||||
 | 
			
		||||
	// If we ran out of calls and the reset time is still ahead of us
 | 
			
		||||
	// then we need to take it easy and relax a little
 | 
			
		||||
	if b.Remaining < minRemaining && b.reset.After(time.Now()) {
 | 
			
		||||
		return b.reset.Sub(time.Now())
 | 
			
		||||
	if b.remaining < 1 && b.reset.After(time.Now()) {
 | 
			
		||||
		time.Sleep(b.reset.Sub(time.Now()))
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for global ratelimits
 | 
			
		||||
	sleepTo := time.Unix(0, atomic.LoadInt64(r.global))
 | 
			
		||||
	if now := time.Now(); now.Before(sleepTo) {
 | 
			
		||||
		return sleepTo.Sub(now)
 | 
			
		||||
		time.Sleep(sleepTo.Sub(now))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LockBucket Locks until a request can be made
 | 
			
		||||
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
 | 
			
		||||
	return r.LockBucketObject(r.GetBucket(bucketID))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LockBucketObject Locks an already resolved bucket until a request can be made
 | 
			
		||||
func (r *RateLimiter) LockBucketObject(b *Bucket) *Bucket {
 | 
			
		||||
	b.Lock()
 | 
			
		||||
 | 
			
		||||
	if wait := r.GetWaitTime(b, 1); wait > 0 {
 | 
			
		||||
		time.Sleep(wait)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.Remaining--
 | 
			
		||||
	b.remaining--
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,14 +96,13 @@ func (r *RateLimiter) LockBucketObject(b *Bucket) *Bucket {
 | 
			
		||||
type Bucket struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	Key       string
 | 
			
		||||
	Remaining int
 | 
			
		||||
	remaining int
 | 
			
		||||
	limit     int
 | 
			
		||||
	reset     time.Time
 | 
			
		||||
	global    *int64
 | 
			
		||||
 | 
			
		||||
	lastReset       time.Time
 | 
			
		||||
	customRateLimit *customRateLimit
 | 
			
		||||
	Userdata        interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
 | 
			
		||||
@@ -124,10 +113,10 @@ func (b *Bucket) Release(headers http.Header) error {
 | 
			
		||||
	// Check if the bucket uses a custom ratelimiter
 | 
			
		||||
	if rl := b.customRateLimit; rl != nil {
 | 
			
		||||
		if time.Now().Sub(b.lastReset) >= rl.reset {
 | 
			
		||||
			b.Remaining = rl.requests - 1
 | 
			
		||||
			b.remaining = rl.requests - 1
 | 
			
		||||
			b.lastReset = time.Now()
 | 
			
		||||
		}
 | 
			
		||||
		if b.Remaining < 1 {
 | 
			
		||||
		if b.remaining < 1 {
 | 
			
		||||
			b.reset = time.Now().Add(rl.reset)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -187,7 +176,7 @@ func (b *Bucket) Release(headers http.Header) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		b.Remaining = int(parsedRemaining)
 | 
			
		||||
		b.remaining = int(parsedRemaining)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								vendor/github.com/bwmarrin/discordgo/restapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/bwmarrin/discordgo/restapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -65,11 +65,9 @@ func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID
 | 
			
		||||
	if bucketID == "" {
 | 
			
		||||
		bucketID = strings.SplitN(urlStr, "?", 2)[0]
 | 
			
		||||
	}
 | 
			
		||||
	return s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucket(bucketID), sequence)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestWithLockedBucket makes a request using a bucket that's already been locked
 | 
			
		||||
func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b []byte, bucket *Bucket, sequence int) (response []byte, err error) {
 | 
			
		||||
	bucket := s.ratelimiter.LockBucket(bucketID)
 | 
			
		||||
 | 
			
		||||
	if s.Debug {
 | 
			
		||||
		log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
 | 
			
		||||
		log.Printf("API REQUEST  PAYLOAD :: [%s]\n", string(b))
 | 
			
		||||
@@ -141,7 +139,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
 | 
			
		||||
		if sequence < s.MaxRestRetries {
 | 
			
		||||
 | 
			
		||||
			s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status)
 | 
			
		||||
			response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence+1)
 | 
			
		||||
			response, err = s.request(method, urlStr, contentType, b, bucketID, sequence+1)
 | 
			
		||||
		} else {
 | 
			
		||||
			err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response)
 | 
			
		||||
		}
 | 
			
		||||
@@ -160,7 +158,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
 | 
			
		||||
		// we can make the above smarter
 | 
			
		||||
		// this method can cause longer delays than required
 | 
			
		||||
 | 
			
		||||
		response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence)
 | 
			
		||||
		response, err = s.request(method, urlStr, contentType, b, bucketID, sequence)
 | 
			
		||||
 | 
			
		||||
	default: // Error condition
 | 
			
		||||
		err = newRestError(req, resp, response)
 | 
			
		||||
@@ -587,7 +585,7 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) {
 | 
			
		||||
		Name string `json:"name"`
 | 
			
		||||
	}{name}
 | 
			
		||||
 | 
			
		||||
	body, err := s.RequestWithBucketID("POST", EndpointGuildCreate, data, EndpointGuildCreate)
 | 
			
		||||
	body, err := s.RequestWithBucketID("POST", EndpointGuilds, data, EndpointGuilds)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -909,7 +907,7 @@ func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err
 | 
			
		||||
// GuildInvites returns an array of Invite structures for the given guild
 | 
			
		||||
// guildID   : The ID of a Guild.
 | 
			
		||||
func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) {
 | 
			
		||||
	body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInvites(guildID))
 | 
			
		||||
	body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInivtes(guildID))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -959,7 +957,6 @@ func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist b
 | 
			
		||||
	// Prevent sending a color int that is too big.
 | 
			
		||||
	if color > 0xFFFFFF {
 | 
			
		||||
		err = fmt.Errorf("color value cannot be larger than 0xFFFFFF")
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := struct {
 | 
			
		||||
@@ -1023,9 +1020,6 @@ func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, er
 | 
			
		||||
 | 
			
		||||
	uri := EndpointGuildPrune(guildID) + fmt.Sprintf("?days=%d", days)
 | 
			
		||||
	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = unmarshal(body, &p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -1210,7 +1204,7 @@ func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string)
 | 
			
		||||
// Functions specific to Discord Channels
 | 
			
		||||
// ------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Channel returns a Channel structure of a specific Channel.
 | 
			
		||||
// Channel returns a Channel strucutre of a specific Channel.
 | 
			
		||||
// channelID  : The ID of the Channel you want returned.
 | 
			
		||||
func (s *Session) Channel(channelID string) (st *Channel, err error) {
 | 
			
		||||
	body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID))
 | 
			
		||||
@@ -1225,16 +1219,12 @@ func (s *Session) Channel(channelID string) (st *Channel, err error) {
 | 
			
		||||
// ChannelEdit edits the given channel
 | 
			
		||||
// channelID  : The ID of a Channel
 | 
			
		||||
// name       : The new name to assign the channel.
 | 
			
		||||
func (s *Session) ChannelEdit(channelID, name string) (*Channel, error) {
 | 
			
		||||
	return s.ChannelEditComplex(channelID, &ChannelEdit{
 | 
			
		||||
		Name: name,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) {
 | 
			
		||||
 | 
			
		||||
	data := struct {
 | 
			
		||||
		Name string `json:"name"`
 | 
			
		||||
	}{name}
 | 
			
		||||
 | 
			
		||||
// ChannelEditComplex edits an existing channel, replacing the parameters entirely with ChannelEdit struct
 | 
			
		||||
// channelID  : The ID of a Channel
 | 
			
		||||
// data          : The channel struct to send
 | 
			
		||||
func (s *Session) ChannelEditComplex(channelID string, data *ChannelEdit) (st *Channel, err error) {
 | 
			
		||||
	body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
@@ -1486,7 +1476,7 @@ func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs.
 | 
			
		||||
// If only one messageID is in the slice call channelMessageDelete function.
 | 
			
		||||
// If only one messageID is in the slice call channelMessageDelete funciton.
 | 
			
		||||
// If the slice is empty do nothing.
 | 
			
		||||
// channelID : The ID of the channel for the messages to delete.
 | 
			
		||||
// messages  : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages.
 | 
			
		||||
@@ -1579,14 +1569,16 @@ func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) {
 | 
			
		||||
 | 
			
		||||
// ChannelInviteCreate creates a new invite for the given channel.
 | 
			
		||||
// channelID   : The ID of a Channel
 | 
			
		||||
// i           : An Invite struct with the values MaxAge, MaxUses and Temporary defined.
 | 
			
		||||
// i           : An Invite struct with the values MaxAge, MaxUses, Temporary,
 | 
			
		||||
//               and XkcdPass defined.
 | 
			
		||||
func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) {
 | 
			
		||||
 | 
			
		||||
	data := struct {
 | 
			
		||||
		MaxAge    int  `json:"max_age"`
 | 
			
		||||
		MaxUses   int  `json:"max_uses"`
 | 
			
		||||
		Temporary bool `json:"temporary"`
 | 
			
		||||
	}{i.MaxAge, i.MaxUses, i.Temporary}
 | 
			
		||||
		MaxAge    int    `json:"max_age"`
 | 
			
		||||
		MaxUses   int    `json:"max_uses"`
 | 
			
		||||
		Temporary bool   `json:"temporary"`
 | 
			
		||||
		XKCDPass  string `json:"xkcdpass"`
 | 
			
		||||
	}{i.MaxAge, i.MaxUses, i.Temporary, i.XkcdPass}
 | 
			
		||||
 | 
			
		||||
	body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -1626,7 +1618,7 @@ func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error
 | 
			
		||||
// ------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Invite returns an Invite structure of the given invite
 | 
			
		||||
// inviteID : The invite code
 | 
			
		||||
// inviteID : The invite code (or maybe xkcdpass?)
 | 
			
		||||
func (s *Session) Invite(inviteID string) (st *Invite, err error) {
 | 
			
		||||
 | 
			
		||||
	body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
			
		||||
@@ -1639,7 +1631,7 @@ func (s *Session) Invite(inviteID string) (st *Invite, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteDelete deletes an existing invite
 | 
			
		||||
// inviteID   : the code of an invite
 | 
			
		||||
// inviteID   : the code (or maybe xkcdpass?) of an invite
 | 
			
		||||
func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
 | 
			
		||||
 | 
			
		||||
	body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
			
		||||
@@ -1652,7 +1644,7 @@ func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteAccept accepts an Invite to a Guild or Channel
 | 
			
		||||
// inviteID : The invite code
 | 
			
		||||
// inviteID : The invite code (or maybe xkcdpass?)
 | 
			
		||||
func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) {
 | 
			
		||||
 | 
			
		||||
	body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite(""))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/state.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/state.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -531,7 +531,7 @@ func (s *State) PrivateChannel(channelID string) (*Channel, error) {
 | 
			
		||||
	return s.Channel(channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Channel gets a channel by ID, it will look in all guilds and private channels.
 | 
			
		||||
// Channel gets a channel by ID, it will look in all guilds an private channels.
 | 
			
		||||
func (s *State) Channel(channelID string) (*Channel, error) {
 | 
			
		||||
	if s == nil {
 | 
			
		||||
		return nil, ErrNilState
 | 
			
		||||
@@ -816,13 +816,6 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
 | 
			
		||||
		if s.TrackMembers {
 | 
			
		||||
			err = s.MemberRemove(t.Member)
 | 
			
		||||
		}
 | 
			
		||||
	case *GuildMembersChunk:
 | 
			
		||||
		if s.TrackMembers {
 | 
			
		||||
			for i := range t.Members {
 | 
			
		||||
				t.Members[i].GuildID = t.GuildID
 | 
			
		||||
				err = s.MemberAdd(t.Members[i])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case *GuildRoleCreate:
 | 
			
		||||
		if s.TrackRoles {
 | 
			
		||||
			err = s.RoleAdd(t.GuildID, t.Role)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								vendor/github.com/bwmarrin/discordgo/structs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										98
									
								
								vendor/github.com/bwmarrin/discordgo/structs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -14,6 +14,7 @@ package discordgo
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -84,9 +85,6 @@ type Session struct {
 | 
			
		||||
	// Stores the last HeartbeatAck that was recieved (in UTC)
 | 
			
		||||
	LastHeartbeatAck time.Time
 | 
			
		||||
 | 
			
		||||
	// used to deal with rate limits
 | 
			
		||||
	Ratelimiter *RateLimiter
 | 
			
		||||
 | 
			
		||||
	// Event handlers
 | 
			
		||||
	handlersMu   sync.RWMutex
 | 
			
		||||
	handlers     map[string][]*eventHandlerInstance
 | 
			
		||||
@@ -98,6 +96,9 @@ type Session struct {
 | 
			
		||||
	// When nil, the session is not listening.
 | 
			
		||||
	listening chan interface{}
 | 
			
		||||
 | 
			
		||||
	// used to deal with rate limits
 | 
			
		||||
	ratelimiter *RateLimiter
 | 
			
		||||
 | 
			
		||||
	// sequence tracks the current gateway api websocket sequence number
 | 
			
		||||
	sequence *int64
 | 
			
		||||
 | 
			
		||||
@@ -142,9 +143,9 @@ type Invite struct {
 | 
			
		||||
	MaxAge    int       `json:"max_age"`
 | 
			
		||||
	Uses      int       `json:"uses"`
 | 
			
		||||
	MaxUses   int       `json:"max_uses"`
 | 
			
		||||
	XkcdPass  string    `json:"xkcdpass"`
 | 
			
		||||
	Revoked   bool      `json:"revoked"`
 | 
			
		||||
	Temporary bool      `json:"temporary"`
 | 
			
		||||
	Unique    bool      `json:"unique"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelType is the type of a Channel
 | 
			
		||||
@@ -170,22 +171,9 @@ type Channel struct {
 | 
			
		||||
	NSFW                 bool                   `json:"nsfw"`
 | 
			
		||||
	Position             int                    `json:"position"`
 | 
			
		||||
	Bitrate              int                    `json:"bitrate"`
 | 
			
		||||
	Recipients           []*User                `json:"recipients"`
 | 
			
		||||
	Recipients           []*User                `json:"recipient"`
 | 
			
		||||
	Messages             []*Message             `json:"-"`
 | 
			
		||||
	PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
 | 
			
		||||
	ParentID             string                 `json:"parent_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A ChannelEdit holds Channel Feild data for a channel edit.
 | 
			
		||||
type ChannelEdit struct {
 | 
			
		||||
	Name                 string                 `json:"name,omitempty"`
 | 
			
		||||
	Topic                string                 `json:"topic,omitempty"`
 | 
			
		||||
	NSFW                 bool                   `json:"nsfw,omitempty"`
 | 
			
		||||
	Position             int                    `json:"position"`
 | 
			
		||||
	Bitrate              int                    `json:"bitrate,omitempty"`
 | 
			
		||||
	UserLimit            int                    `json:"user_limit,omitempty"`
 | 
			
		||||
	PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
 | 
			
		||||
	ParentID             string                 `json:"parent_id,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A PermissionOverwrite holds permission overwrite data for a Channel
 | 
			
		||||
@@ -203,7 +191,6 @@ type Emoji struct {
 | 
			
		||||
	Roles         []string `json:"roles"`
 | 
			
		||||
	Managed       bool     `json:"managed"`
 | 
			
		||||
	RequireColons bool     `json:"require_colons"`
 | 
			
		||||
	Animated      bool     `json:"animated"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
 | 
			
		||||
@@ -217,7 +204,7 @@ func (e *Emoji) APIName() string {
 | 
			
		||||
	return e.ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VerificationLevel type definition
 | 
			
		||||
// VerificationLevel type defination
 | 
			
		||||
type VerificationLevel int
 | 
			
		||||
 | 
			
		||||
// Constants for VerificationLevel levels from 0 to 3 inclusive
 | 
			
		||||
@@ -327,56 +314,43 @@ type Presence struct {
 | 
			
		||||
	Since  *int     `json:"since"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GameType is the type of "game" (see GameType* consts) in the Game struct
 | 
			
		||||
type GameType int
 | 
			
		||||
 | 
			
		||||
// Valid GameType values
 | 
			
		||||
const (
 | 
			
		||||
	GameTypeGame GameType = iota
 | 
			
		||||
	GameTypeStreaming
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Game struct holds the name of the "playing .." game for a user
 | 
			
		||||
type Game struct {
 | 
			
		||||
	Name          string     `json:"name"`
 | 
			
		||||
	Type          GameType   `json:"type"`
 | 
			
		||||
	URL           string     `json:"url,omitempty"`
 | 
			
		||||
	Details       string     `json:"details,omitempty"`
 | 
			
		||||
	State         string     `json:"state,omitempty"`
 | 
			
		||||
	TimeStamps    TimeStamps `json:"timestamps,omitempty"`
 | 
			
		||||
	Assets        Assets     `json:"assets,omitempty"`
 | 
			
		||||
	ApplicationID string     `json:"application_id,omitempty"`
 | 
			
		||||
	Instance      int8       `json:"instance,omitempty"`
 | 
			
		||||
	// TODO: Party and Secrets (unknown structure)
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Type int    `json:"type"`
 | 
			
		||||
	URL  string `json:"url,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A TimeStamps struct contains start and end times used in the rich presence "playing .." Game
 | 
			
		||||
type TimeStamps struct {
 | 
			
		||||
	EndTimestamp   int64 `json:"end,omitempty"`
 | 
			
		||||
	StartTimestamp int64 `json:"start,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON unmarshals JSON into TimeStamps struct
 | 
			
		||||
func (t *TimeStamps) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	temp := struct {
 | 
			
		||||
		End   float64 `json:"end,omitempty"`
 | 
			
		||||
		Start float64 `json:"start,omitempty"`
 | 
			
		||||
// UnmarshalJSON unmarshals json to Game struct
 | 
			
		||||
func (g *Game) UnmarshalJSON(bytes []byte) error {
 | 
			
		||||
	temp := &struct {
 | 
			
		||||
		Name json.Number     `json:"name"`
 | 
			
		||||
		Type json.RawMessage `json:"type"`
 | 
			
		||||
		URL  string          `json:"url"`
 | 
			
		||||
	}{}
 | 
			
		||||
	err := json.Unmarshal(b, &temp)
 | 
			
		||||
	err := json.Unmarshal(bytes, temp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	t.EndTimestamp = int64(temp.End)
 | 
			
		||||
	t.StartTimestamp = int64(temp.Start)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
	g.URL = temp.URL
 | 
			
		||||
	g.Name = temp.Name.String()
 | 
			
		||||
 | 
			
		||||
// An Assets struct contains assets and labels used in the rich presence "playing .." Game
 | 
			
		||||
type Assets struct {
 | 
			
		||||
	LargeImageID string `json:"large_image,omitempty"`
 | 
			
		||||
	SmallImageID string `json:"small_image,omitempty"`
 | 
			
		||||
	LargeText    string `json:"large_text,omitempty"`
 | 
			
		||||
	SmallText    string `json:"small_text,omitempty"`
 | 
			
		||||
	if temp.Type != nil {
 | 
			
		||||
		err = json.Unmarshal(temp.Type, &g.Type)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s := ""
 | 
			
		||||
		err = json.Unmarshal(temp.Type, &s)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			g.Type, err = strconv.Atoi(s)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Member stores user information for Guild members.
 | 
			
		||||
@@ -409,7 +383,7 @@ type Settings struct {
 | 
			
		||||
	DeveloperMode          bool               `json:"developer_mode"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Status type definition
 | 
			
		||||
// Status type defination
 | 
			
		||||
type Status string
 | 
			
		||||
 | 
			
		||||
// Constants for Status with the different current available status
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/bwmarrin/discordgo/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -29,9 +29,7 @@ func (u *User) Mention() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AvatarURL returns a URL to the user's avatar.
 | 
			
		||||
//    size:    The size of the user's avatar as a power of two
 | 
			
		||||
//             if size is an empty string, no size parameter will
 | 
			
		||||
//             be added to the URL.
 | 
			
		||||
//		size:     The size of the user's avatar as a power of two
 | 
			
		||||
func (u *User) AvatarURL(size string) string {
 | 
			
		||||
	var URL string
 | 
			
		||||
	if strings.HasPrefix(u.Avatar, "a_") {
 | 
			
		||||
@@ -40,8 +38,5 @@ func (u *User) AvatarURL(size string) string {
 | 
			
		||||
		URL = EndpointUserAvatar(u.ID, u.Avatar)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if size != "" {
 | 
			
		||||
		return URL + "?size=" + size
 | 
			
		||||
	}
 | 
			
		||||
	return URL
 | 
			
		||||
	return URL + "?size=" + size
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/bwmarrin/discordgo/voice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/bwmarrin/discordgo/voice.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,7 @@ import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -68,7 +69,7 @@ type VoiceConnection struct {
 | 
			
		||||
	voiceSpeakingUpdateHandlers []VoiceSpeakingUpdateHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VoiceSpeakingUpdateHandler type provides a function definition for the
 | 
			
		||||
// VoiceSpeakingUpdateHandler type provides a function defination for the
 | 
			
		||||
// VoiceSpeakingUpdate event
 | 
			
		||||
type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdate)
 | 
			
		||||
 | 
			
		||||
@@ -103,7 +104,7 @@ func (v *VoiceConnection) Speaking(b bool) (err error) {
 | 
			
		||||
	defer v.Unlock()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		v.speaking = false
 | 
			
		||||
		v.log(LogError, "Speaking() write json error:", err)
 | 
			
		||||
		log.Println("Speaking() write json error:", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -180,7 +181,7 @@ func (v *VoiceConnection) Close() {
 | 
			
		||||
		v.log(LogInformational, "closing udp")
 | 
			
		||||
		err := v.udpConn.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			v.log(LogError, "error closing udp connection: ", err)
 | 
			
		||||
			log.Println("error closing udp connection: ", err)
 | 
			
		||||
		}
 | 
			
		||||
		v.udpConn = nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -246,7 +247,7 @@ type voiceOP2 struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitUntilConnected waits for the Voice Connection to
 | 
			
		||||
// become ready, if it does not become ready it returns an err
 | 
			
		||||
// become ready, if it does not become ready it retuns an err
 | 
			
		||||
func (v *VoiceConnection) waitUntilConnected() error {
 | 
			
		||||
 | 
			
		||||
	v.log(LogInformational, "called")
 | 
			
		||||
@@ -857,7 +858,7 @@ func (v *VoiceConnection) reconnect() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if v.session.DataReady == false || v.session.wsConn == nil {
 | 
			
		||||
			v.log(LogInformational, "cannot reconnect to channel %s with unready session", v.ChannelID)
 | 
			
		||||
			v.log(LogInformational, "cannot reconenct to channel %s with unready session", v.ChannelID)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										253
									
								
								vendor/github.com/bwmarrin/discordgo/wsapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										253
									
								
								vendor/github.com/bwmarrin/discordgo/wsapi.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,7 +15,6 @@ import (
 | 
			
		||||
	"compress/zlib"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"runtime"
 | 
			
		||||
@@ -46,114 +45,19 @@ type resumePacket struct {
 | 
			
		||||
	} `json:"d"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open creates a websocket connection to Discord.
 | 
			
		||||
// See: https://discordapp.com/developers/docs/topics/gateway#connecting
 | 
			
		||||
func (s *Session) Open() error {
 | 
			
		||||
// Open opens a websocket connection to Discord.
 | 
			
		||||
func (s *Session) Open() (err error) {
 | 
			
		||||
 | 
			
		||||
	s.log(LogInformational, "called")
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	// Prevent Open or other major Session functions from
 | 
			
		||||
	// being called while Open is still running.
 | 
			
		||||
	s.Lock()
 | 
			
		||||
	defer s.Unlock()
 | 
			
		||||
 | 
			
		||||
	// If the websock is already open, bail out here.
 | 
			
		||||
	if s.wsConn != nil {
 | 
			
		||||
		return ErrWSAlreadyOpen
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the gateway to use for the Websocket connection
 | 
			
		||||
	if s.gateway == "" {
 | 
			
		||||
		s.gateway, err = s.Gateway()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Add the version and encoding to the URL
 | 
			
		||||
		s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Connect to the Gateway
 | 
			
		||||
	s.log(LogInformational, "connecting to gateway %s", s.gateway)
 | 
			
		||||
	header := http.Header{}
 | 
			
		||||
	header.Add("accept-encoding", "zlib")
 | 
			
		||||
	s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
 | 
			
		||||
		s.gateway = "" // clear cached gateway
 | 
			
		||||
		s.wsConn = nil // Just to be safe.
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		// because of this, all code below must set err to the error
 | 
			
		||||
		// when exiting with an error :)  Maybe someone has a better
 | 
			
		||||
		// way :)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.wsConn.Close()
 | 
			
		||||
			s.wsConn = nil
 | 
			
		||||
			s.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// The first response from Discord should be an Op 10 (Hello) Packet.
 | 
			
		||||
	// When processed by onEvent the heartbeat goroutine will be started.
 | 
			
		||||
	mt, m, err := s.wsConn.ReadMessage()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	e, err := s.onEvent(mt, m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if e.Operation != 10 {
 | 
			
		||||
		err = fmt.Errorf("expecting Op 10, got Op %d instead", e.Operation)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	s.log(LogInformational, "Op 10 Hello Packet received from Discord")
 | 
			
		||||
	s.LastHeartbeatAck = time.Now().UTC()
 | 
			
		||||
	var h helloOp
 | 
			
		||||
	if err = json.Unmarshal(e.RawData, &h); err != nil {
 | 
			
		||||
		err = fmt.Errorf("error unmarshalling helloOp, %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now we send either an Op 2 Identity if this is a brand new
 | 
			
		||||
	// connection or Op 6 Resume if we are resuming an existing connection.
 | 
			
		||||
	sequence := atomic.LoadInt64(s.sequence)
 | 
			
		||||
	if s.sessionID == "" && sequence == 0 {
 | 
			
		||||
 | 
			
		||||
		// Send Op 2 Identity Packet
 | 
			
		||||
		err = s.identify()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err = fmt.Errorf("error sending identify packet to gateway, %s, %s", s.gateway, err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
		// Send Op 6 Resume Packet
 | 
			
		||||
		p := resumePacket{}
 | 
			
		||||
		p.Op = 6
 | 
			
		||||
		p.Data.Token = s.Token
 | 
			
		||||
		p.Data.SessionID = s.sessionID
 | 
			
		||||
		p.Data.Sequence = sequence
 | 
			
		||||
 | 
			
		||||
		s.log(LogInformational, "sending resume packet to gateway")
 | 
			
		||||
		s.wsMutex.Lock()
 | 
			
		||||
		err = s.wsConn.WriteJSON(p)
 | 
			
		||||
		s.wsMutex.Unlock()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err = fmt.Errorf("error sending gateway resume packet, %s, %s", s.gateway, err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A basic state is a hard requirement for Voice.
 | 
			
		||||
	// We create it here so the below READY/RESUMED packet can populate
 | 
			
		||||
	// the state :)
 | 
			
		||||
	// XXX: Move to New() func?
 | 
			
		||||
	if s.State == nil {
 | 
			
		||||
		state := NewState()
 | 
			
		||||
		state.TrackChannels = false
 | 
			
		||||
@@ -164,42 +68,77 @@ func (s *Session) Open() error {
 | 
			
		||||
		s.State = state
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now Discord should send us a READY or RESUMED packet.
 | 
			
		||||
	mt, m, err = s.wsConn.ReadMessage()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	if s.wsConn != nil {
 | 
			
		||||
		err = ErrWSAlreadyOpen
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	e, err = s.onEvent(mt, m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if e.Type != `READY` && e.Type != `RESUMED` {
 | 
			
		||||
		// This is not fatal, but it does not follow their API documentation.
 | 
			
		||||
		s.log(LogWarning, "Expected READY/RESUMED, instead got:\n%#v\n", e)
 | 
			
		||||
	}
 | 
			
		||||
	s.log(LogInformational, "First Packet:\n%#v\n", e)
 | 
			
		||||
 | 
			
		||||
	s.log(LogInformational, "We are now connected to Discord, emitting connect event")
 | 
			
		||||
	s.handleEvent(connectEventType, &Connect{})
 | 
			
		||||
 | 
			
		||||
	// A VoiceConnections map is a hard requirement for Voice.
 | 
			
		||||
	// XXX: can this be moved to when opening a voice connection?
 | 
			
		||||
	if s.VoiceConnections == nil {
 | 
			
		||||
		s.log(LogInformational, "creating new VoiceConnections map")
 | 
			
		||||
		s.VoiceConnections = make(map[string]*VoiceConnection)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create listening chan outside of listen, as it needs to happen inside the
 | 
			
		||||
	// mutex lock and needs to exist before calling heartbeat and listen
 | 
			
		||||
	// go rountines.
 | 
			
		||||
	s.listening = make(chan interface{})
 | 
			
		||||
	// Get the gateway to use for the Websocket connection
 | 
			
		||||
	if s.gateway == "" {
 | 
			
		||||
		s.gateway, err = s.Gateway()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	// Start sending heartbeats and reading messages from Discord.
 | 
			
		||||
	go s.heartbeat(s.wsConn, s.listening, h.HeartbeatInterval)
 | 
			
		||||
		// Add the version and encoding to the URL
 | 
			
		||||
		s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	header := http.Header{}
 | 
			
		||||
	header.Add("accept-encoding", "zlib")
 | 
			
		||||
 | 
			
		||||
	s.log(LogInformational, "connecting to gateway %s", s.gateway)
 | 
			
		||||
	s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
 | 
			
		||||
		s.gateway = "" // clear cached gateway
 | 
			
		||||
		// TODO: should we add a retry block here?
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sequence := atomic.LoadInt64(s.sequence)
 | 
			
		||||
	if s.sessionID != "" && sequence > 0 {
 | 
			
		||||
 | 
			
		||||
		p := resumePacket{}
 | 
			
		||||
		p.Op = 6
 | 
			
		||||
		p.Data.Token = s.Token
 | 
			
		||||
		p.Data.SessionID = s.sessionID
 | 
			
		||||
		p.Data.Sequence = sequence
 | 
			
		||||
 | 
			
		||||
		s.log(LogInformational, "sending resume packet to gateway")
 | 
			
		||||
		err = s.wsConn.WriteJSON(p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.log(LogWarning, "error sending gateway resume packet, %s, %s", s.gateway, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
		err = s.identify()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create listening outside of listen, as it needs to happen inside the mutex
 | 
			
		||||
	// lock.
 | 
			
		||||
	s.listening = make(chan interface{})
 | 
			
		||||
	go s.listen(s.wsConn, s.listening)
 | 
			
		||||
	s.LastHeartbeatAck = time.Now().UTC()
 | 
			
		||||
 | 
			
		||||
	s.Unlock()
 | 
			
		||||
 | 
			
		||||
	s.log(LogInformational, "emit connect event")
 | 
			
		||||
	s.handleEvent(connectEventType, &Connect{})
 | 
			
		||||
 | 
			
		||||
	s.log(LogInformational, "exiting")
 | 
			
		||||
	return nil
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// listen polls the websocket connection for events, it will stop when the
 | 
			
		||||
@@ -310,8 +249,7 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateStatusData ia provided to UpdateStatusComplex()
 | 
			
		||||
type UpdateStatusData struct {
 | 
			
		||||
type updateStatusData struct {
 | 
			
		||||
	IdleSince *int   `json:"since"`
 | 
			
		||||
	Game      *Game  `json:"game"`
 | 
			
		||||
	AFK       bool   `json:"afk"`
 | 
			
		||||
@@ -320,7 +258,7 @@ type UpdateStatusData struct {
 | 
			
		||||
 | 
			
		||||
type updateStatusOp struct {
 | 
			
		||||
	Op   int              `json:"op"`
 | 
			
		||||
	Data UpdateStatusData `json:"d"`
 | 
			
		||||
	Data updateStatusData `json:"d"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateStreamingStatus is used to update the user's streaming status.
 | 
			
		||||
@@ -332,7 +270,13 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
 | 
			
		||||
 | 
			
		||||
	s.log(LogInformational, "called")
 | 
			
		||||
 | 
			
		||||
	usd := UpdateStatusData{
 | 
			
		||||
	s.RLock()
 | 
			
		||||
	defer s.RUnlock()
 | 
			
		||||
	if s.wsConn == nil {
 | 
			
		||||
		return ErrWSNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	usd := updateStatusData{
 | 
			
		||||
		Status: "online",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -341,9 +285,9 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if game != "" {
 | 
			
		||||
		gameType := GameTypeGame
 | 
			
		||||
		gameType := 0
 | 
			
		||||
		if url != "" {
 | 
			
		||||
			gameType = GameTypeStreaming
 | 
			
		||||
			gameType = 1
 | 
			
		||||
		}
 | 
			
		||||
		usd.Game = &Game{
 | 
			
		||||
			Name: game,
 | 
			
		||||
@@ -352,18 +296,6 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.UpdateStatusComplex(usd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateStatusComplex allows for sending the raw status update data untouched by discordgo.
 | 
			
		||||
func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
 | 
			
		||||
 | 
			
		||||
	s.RLock()
 | 
			
		||||
	defer s.RUnlock()
 | 
			
		||||
	if s.wsConn == nil {
 | 
			
		||||
		return ErrWSNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.wsMutex.Lock()
 | 
			
		||||
	err = s.wsConn.WriteJSON(updateStatusOp{3, usd})
 | 
			
		||||
	s.wsMutex.Unlock()
 | 
			
		||||
@@ -425,7 +357,9 @@ func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err err
 | 
			
		||||
//
 | 
			
		||||
// If you use the AddHandler() function to register a handler for the
 | 
			
		||||
// "OnEvent" event then all events will be passed to that handler.
 | 
			
		||||
func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
//
 | 
			
		||||
// TODO: You may also register a custom event handler entirely using...
 | 
			
		||||
func (s *Session) onEvent(messageType int, message []byte) {
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	var reader io.Reader
 | 
			
		||||
@@ -437,7 +371,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
		z, err2 := zlib.NewReader(reader)
 | 
			
		||||
		if err2 != nil {
 | 
			
		||||
			s.log(LogError, "error uncompressing websocket message, %s", err)
 | 
			
		||||
			return nil, err2
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		defer func() {
 | 
			
		||||
@@ -455,7 +389,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
	decoder := json.NewDecoder(reader)
 | 
			
		||||
	if err = decoder.Decode(&e); err != nil {
 | 
			
		||||
		s.log(LogError, "error decoding websocket message, %s", err)
 | 
			
		||||
		return e, err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.log(LogDebug, "Op: %d, Seq: %d, Type: %s, Data: %s\n\n", e.Operation, e.Sequence, e.Type, string(e.RawData))
 | 
			
		||||
@@ -469,10 +403,10 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
		s.wsMutex.Unlock()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.log(LogError, "error sending heartbeat in response to Op1")
 | 
			
		||||
			return e, err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return e, nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reconnect
 | 
			
		||||
@@ -481,7 +415,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
		s.log(LogInformational, "Closing and reconnecting in response to Op7")
 | 
			
		||||
		s.Close()
 | 
			
		||||
		s.reconnect()
 | 
			
		||||
		return e, nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Invalid Session
 | 
			
		||||
@@ -493,15 +427,20 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
		err = s.identify()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
 | 
			
		||||
			return e, err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return e, nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if e.Operation == 10 {
 | 
			
		||||
		// Op10 is handled by Open()
 | 
			
		||||
		return e, nil
 | 
			
		||||
		var h helloOp
 | 
			
		||||
		if err = json.Unmarshal(e.RawData, &h); err != nil {
 | 
			
		||||
			s.log(LogError, "error unmarshalling helloOp, %s", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			go s.heartbeat(s.wsConn, s.listening, h.HeartbeatInterval)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if e.Operation == 11 {
 | 
			
		||||
@@ -509,7 +448,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
		s.LastHeartbeatAck = time.Now().UTC()
 | 
			
		||||
		s.Unlock()
 | 
			
		||||
		s.log(LogInformational, "got heartbeat ACK")
 | 
			
		||||
		return e, nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Do not try to Dispatch a non-Dispatch Message
 | 
			
		||||
@@ -517,7 +456,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
		// But we probably should be doing something with them.
 | 
			
		||||
		// TEMP
 | 
			
		||||
		s.log(LogWarning, "unknown Op: %d, Seq: %d, Type: %s, Data: %s, message: %s", e.Operation, e.Sequence, e.Type, string(e.RawData), string(message))
 | 
			
		||||
		return e, nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Store the message sequence
 | 
			
		||||
@@ -546,8 +485,6 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
 | 
			
		||||
 | 
			
		||||
	// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
 | 
			
		||||
	s.handleEvent(eventEventType, e)
 | 
			
		||||
 | 
			
		||||
	return e, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------------------------------------------------------------------------------------
 | 
			
		||||
@@ -673,7 +610,7 @@ func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
 | 
			
		||||
	voice.GuildID = st.GuildID
 | 
			
		||||
	voice.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Open a connection to the voice server
 | 
			
		||||
	// Open a conenction to the voice server
 | 
			
		||||
	err := voice.open()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										77
									
								
								vendor/github.com/gorilla/websocket/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/gorilla/websocket/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,77 +0,0 @@
 | 
			
		||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type netDialerFunc func(netowrk, addr string) (net.Conn, error)
 | 
			
		||||
 | 
			
		||||
func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return fn(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
 | 
			
		||||
		return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type httpProxyDialer struct {
 | 
			
		||||
	proxyURL   *url.URL
 | 
			
		||||
	fowardDial func(network, addr string) (net.Conn, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
 | 
			
		||||
	hostPort, _ := hostPortNoPort(hpd.proxyURL)
 | 
			
		||||
	conn, err := hpd.fowardDial(network, hostPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	connectHeader := make(http.Header)
 | 
			
		||||
	if user := hpd.proxyURL.User; user != nil {
 | 
			
		||||
		proxyUser := user.Username()
 | 
			
		||||
		if proxyPassword, passwordSet := user.Password(); passwordSet {
 | 
			
		||||
			credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
 | 
			
		||||
			connectHeader.Set("Proxy-Authorization", "Basic "+credential)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	connectReq := &http.Request{
 | 
			
		||||
		Method: "CONNECT",
 | 
			
		||||
		URL:    &url.URL{Opaque: addr},
 | 
			
		||||
		Host:   addr,
 | 
			
		||||
		Header: connectHeader,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := connectReq.Write(conn); err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read response. It's OK to use and discard buffered reader here becaue
 | 
			
		||||
	// the remote server does not speak until spoken to.
 | 
			
		||||
	br := bufio.NewReader(conn)
 | 
			
		||||
	resp, err := http.ReadResponse(br, connectReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		f := strings.SplitN(resp.Status, " ", 2)
 | 
			
		||||
		return nil, errors.New(f[1])
 | 
			
		||||
	}
 | 
			
		||||
	return conn, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										473
									
								
								vendor/github.com/gorilla/websocket/x_net_proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										473
									
								
								vendor/github.com/gorilla/websocket/x_net_proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,473 +0,0 @@
 | 
			
		||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
 | 
			
		||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
 | 
			
		||||
 | 
			
		||||
// Package proxy provides support for a variety of protocols to proxy network
 | 
			
		||||
// data.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type proxy_direct struct{}
 | 
			
		||||
 | 
			
		||||
// Direct is a direct proxy: one that makes network connections directly.
 | 
			
		||||
var proxy_Direct = proxy_direct{}
 | 
			
		||||
 | 
			
		||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return net.Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A PerHost directs connections to a default Dialer unless the host name
 | 
			
		||||
// requested matches one of a number of exceptions.
 | 
			
		||||
type proxy_PerHost struct {
 | 
			
		||||
	def, bypass proxy_Dialer
 | 
			
		||||
 | 
			
		||||
	bypassNetworks []*net.IPNet
 | 
			
		||||
	bypassIPs      []net.IP
 | 
			
		||||
	bypassZones    []string
 | 
			
		||||
	bypassHosts    []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPerHost returns a PerHost Dialer that directs connections to either
 | 
			
		||||
// defaultDialer or bypass, depending on whether the connection matches one of
 | 
			
		||||
// the configured rules.
 | 
			
		||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
 | 
			
		||||
	return &proxy_PerHost{
 | 
			
		||||
		def:    defaultDialer,
 | 
			
		||||
		bypass: bypass,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the address addr on the given network through either
 | 
			
		||||
// defaultDialer or bypass.
 | 
			
		||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
 | 
			
		||||
	host, _, err := net.SplitHostPort(addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p.dialerForRequest(host).Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
 | 
			
		||||
	if ip := net.ParseIP(host); ip != nil {
 | 
			
		||||
		for _, net := range p.bypassNetworks {
 | 
			
		||||
			if net.Contains(ip) {
 | 
			
		||||
				return p.bypass
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, bypassIP := range p.bypassIPs {
 | 
			
		||||
			if bypassIP.Equal(ip) {
 | 
			
		||||
				return p.bypass
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return p.def
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, zone := range p.bypassZones {
 | 
			
		||||
		if strings.HasSuffix(host, zone) {
 | 
			
		||||
			return p.bypass
 | 
			
		||||
		}
 | 
			
		||||
		if host == zone[1:] {
 | 
			
		||||
			// For a zone ".example.com", we match "example.com"
 | 
			
		||||
			// too.
 | 
			
		||||
			return p.bypass
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, bypassHost := range p.bypassHosts {
 | 
			
		||||
		if bypassHost == host {
 | 
			
		||||
			return p.bypass
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return p.def
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddFromString parses a string that contains comma-separated values
 | 
			
		||||
// specifying hosts that should use the bypass proxy. Each value is either an
 | 
			
		||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
 | 
			
		||||
// (localhost). A best effort is made to parse the string and errors are
 | 
			
		||||
// ignored.
 | 
			
		||||
func (p *proxy_PerHost) AddFromString(s string) {
 | 
			
		||||
	hosts := strings.Split(s, ",")
 | 
			
		||||
	for _, host := range hosts {
 | 
			
		||||
		host = strings.TrimSpace(host)
 | 
			
		||||
		if len(host) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if strings.Contains(host, "/") {
 | 
			
		||||
			// We assume that it's a CIDR address like 127.0.0.0/8
 | 
			
		||||
			if _, net, err := net.ParseCIDR(host); err == nil {
 | 
			
		||||
				p.AddNetwork(net)
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if ip := net.ParseIP(host); ip != nil {
 | 
			
		||||
			p.AddIP(ip)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if strings.HasPrefix(host, "*.") {
 | 
			
		||||
			p.AddZone(host[1:])
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		p.AddHost(host)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
 | 
			
		||||
// this will only take effect if a literal IP address is dialed. A connection
 | 
			
		||||
// to a named host will never match an IP.
 | 
			
		||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
 | 
			
		||||
	p.bypassIPs = append(p.bypassIPs, ip)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
 | 
			
		||||
// this will only take effect if a literal IP address is dialed. A connection
 | 
			
		||||
// to a named host will never match.
 | 
			
		||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
 | 
			
		||||
	p.bypassNetworks = append(p.bypassNetworks, net)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
 | 
			
		||||
// "example.com" matches "example.com" and all of its subdomains.
 | 
			
		||||
func (p *proxy_PerHost) AddZone(zone string) {
 | 
			
		||||
	if strings.HasSuffix(zone, ".") {
 | 
			
		||||
		zone = zone[:len(zone)-1]
 | 
			
		||||
	}
 | 
			
		||||
	if !strings.HasPrefix(zone, ".") {
 | 
			
		||||
		zone = "." + zone
 | 
			
		||||
	}
 | 
			
		||||
	p.bypassZones = append(p.bypassZones, zone)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddHost specifies a host name that will use the bypass proxy.
 | 
			
		||||
func (p *proxy_PerHost) AddHost(host string) {
 | 
			
		||||
	if strings.HasSuffix(host, ".") {
 | 
			
		||||
		host = host[:len(host)-1]
 | 
			
		||||
	}
 | 
			
		||||
	p.bypassHosts = append(p.bypassHosts, host)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Dialer is a means to establish a connection.
 | 
			
		||||
type proxy_Dialer interface {
 | 
			
		||||
	// Dial connects to the given address via the proxy.
 | 
			
		||||
	Dial(network, addr string) (c net.Conn, err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Auth contains authentication parameters that specific Dialers may require.
 | 
			
		||||
type proxy_Auth struct {
 | 
			
		||||
	User, Password string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromEnvironment returns the dialer specified by the proxy related variables in
 | 
			
		||||
// the environment.
 | 
			
		||||
func proxy_FromEnvironment() proxy_Dialer {
 | 
			
		||||
	allProxy := proxy_allProxyEnv.Get()
 | 
			
		||||
	if len(allProxy) == 0 {
 | 
			
		||||
		return proxy_Direct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	proxyURL, err := url.Parse(allProxy)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return proxy_Direct
 | 
			
		||||
	}
 | 
			
		||||
	proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return proxy_Direct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	noProxy := proxy_noProxyEnv.Get()
 | 
			
		||||
	if len(noProxy) == 0 {
 | 
			
		||||
		return proxy
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	perHost := proxy_NewPerHost(proxy, proxy_Direct)
 | 
			
		||||
	perHost.AddFromString(noProxy)
 | 
			
		||||
	return perHost
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
 | 
			
		||||
// from a URL with such a scheme.
 | 
			
		||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
 | 
			
		||||
 | 
			
		||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
 | 
			
		||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
 | 
			
		||||
// by FromURL.
 | 
			
		||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
 | 
			
		||||
	if proxy_proxySchemes == nil {
 | 
			
		||||
		proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
 | 
			
		||||
	}
 | 
			
		||||
	proxy_proxySchemes[scheme] = f
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromURL returns a Dialer given a URL specification and an underlying
 | 
			
		||||
// Dialer for it to make network requests.
 | 
			
		||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
 | 
			
		||||
	var auth *proxy_Auth
 | 
			
		||||
	if u.User != nil {
 | 
			
		||||
		auth = new(proxy_Auth)
 | 
			
		||||
		auth.User = u.User.Username()
 | 
			
		||||
		if p, ok := u.User.Password(); ok {
 | 
			
		||||
			auth.Password = p
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch u.Scheme {
 | 
			
		||||
	case "socks5":
 | 
			
		||||
		return proxy_SOCKS5("tcp", u.Host, auth, forward)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the scheme doesn't match any of the built-in schemes, see if it
 | 
			
		||||
	// was registered by another package.
 | 
			
		||||
	if proxy_proxySchemes != nil {
 | 
			
		||||
		if f, ok := proxy_proxySchemes[u.Scheme]; ok {
 | 
			
		||||
			return f(u, forward)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	proxy_allProxyEnv = &proxy_envOnce{
 | 
			
		||||
		names: []string{"ALL_PROXY", "all_proxy"},
 | 
			
		||||
	}
 | 
			
		||||
	proxy_noProxyEnv = &proxy_envOnce{
 | 
			
		||||
		names: []string{"NO_PROXY", "no_proxy"},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// envOnce looks up an environment variable (optionally by multiple
 | 
			
		||||
// names) once. It mitigates expensive lookups on some platforms
 | 
			
		||||
// (e.g. Windows).
 | 
			
		||||
// (Borrowed from net/http/transport.go)
 | 
			
		||||
type proxy_envOnce struct {
 | 
			
		||||
	names []string
 | 
			
		||||
	once  sync.Once
 | 
			
		||||
	val   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *proxy_envOnce) Get() string {
 | 
			
		||||
	e.once.Do(e.init)
 | 
			
		||||
	return e.val
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *proxy_envOnce) init() {
 | 
			
		||||
	for _, n := range e.names {
 | 
			
		||||
		e.val = os.Getenv(n)
 | 
			
		||||
		if e.val != "" {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
 | 
			
		||||
// with an optional username and password. See RFC 1928 and RFC 1929.
 | 
			
		||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
 | 
			
		||||
	s := &proxy_socks5{
 | 
			
		||||
		network: network,
 | 
			
		||||
		addr:    addr,
 | 
			
		||||
		forward: forward,
 | 
			
		||||
	}
 | 
			
		||||
	if auth != nil {
 | 
			
		||||
		s.user = auth.User
 | 
			
		||||
		s.password = auth.Password
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type proxy_socks5 struct {
 | 
			
		||||
	user, password string
 | 
			
		||||
	network, addr  string
 | 
			
		||||
	forward        proxy_Dialer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const proxy_socks5Version = 5
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	proxy_socks5AuthNone     = 0
 | 
			
		||||
	proxy_socks5AuthPassword = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const proxy_socks5Connect = 1
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	proxy_socks5IP4    = 1
 | 
			
		||||
	proxy_socks5Domain = 3
 | 
			
		||||
	proxy_socks5IP6    = 4
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var proxy_socks5Errors = []string{
 | 
			
		||||
	"",
 | 
			
		||||
	"general failure",
 | 
			
		||||
	"connection forbidden",
 | 
			
		||||
	"network unreachable",
 | 
			
		||||
	"host unreachable",
 | 
			
		||||
	"connection refused",
 | 
			
		||||
	"TTL expired",
 | 
			
		||||
	"command not supported",
 | 
			
		||||
	"address type not supported",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
 | 
			
		||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	switch network {
 | 
			
		||||
	case "tcp", "tcp6", "tcp4":
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, err := s.forward.Dial(s.network, s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.connect(conn, addr); err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return conn, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// connect takes an existing connection to a socks5 proxy server,
 | 
			
		||||
// and commands the server to extend that connection to target,
 | 
			
		||||
// which must be a canonical address with a host and port.
 | 
			
		||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
 | 
			
		||||
	host, portStr, err := net.SplitHostPort(target)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	port, err := strconv.Atoi(portStr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to parse port number: " + portStr)
 | 
			
		||||
	}
 | 
			
		||||
	if port < 1 || port > 0xffff {
 | 
			
		||||
		return errors.New("proxy: port number out of range: " + portStr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// the size here is just an estimate
 | 
			
		||||
	buf := make([]byte, 0, 6+len(host))
 | 
			
		||||
 | 
			
		||||
	buf = append(buf, proxy_socks5Version)
 | 
			
		||||
	if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
 | 
			
		||||
		buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
 | 
			
		||||
	} else {
 | 
			
		||||
		buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := conn.Write(buf); err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	if buf[0] != 5 {
 | 
			
		||||
		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
 | 
			
		||||
	}
 | 
			
		||||
	if buf[1] == 0xff {
 | 
			
		||||
		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// See RFC 1929
 | 
			
		||||
	if buf[1] == proxy_socks5AuthPassword {
 | 
			
		||||
		buf = buf[:0]
 | 
			
		||||
		buf = append(buf, 1 /* password protocol version */)
 | 
			
		||||
		buf = append(buf, uint8(len(s.user)))
 | 
			
		||||
		buf = append(buf, s.user...)
 | 
			
		||||
		buf = append(buf, uint8(len(s.password)))
 | 
			
		||||
		buf = append(buf, s.password...)
 | 
			
		||||
 | 
			
		||||
		if _, err := conn.Write(buf); err != nil {
 | 
			
		||||
			return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err := io.ReadFull(conn, buf[:2]); err != nil {
 | 
			
		||||
			return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if buf[1] != 0 {
 | 
			
		||||
			return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = buf[:0]
 | 
			
		||||
	buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
 | 
			
		||||
 | 
			
		||||
	if ip := net.ParseIP(host); ip != nil {
 | 
			
		||||
		if ip4 := ip.To4(); ip4 != nil {
 | 
			
		||||
			buf = append(buf, proxy_socks5IP4)
 | 
			
		||||
			ip = ip4
 | 
			
		||||
		} else {
 | 
			
		||||
			buf = append(buf, proxy_socks5IP6)
 | 
			
		||||
		}
 | 
			
		||||
		buf = append(buf, ip...)
 | 
			
		||||
	} else {
 | 
			
		||||
		if len(host) > 255 {
 | 
			
		||||
			return errors.New("proxy: destination host name too long: " + host)
 | 
			
		||||
		}
 | 
			
		||||
		buf = append(buf, proxy_socks5Domain)
 | 
			
		||||
		buf = append(buf, byte(len(host)))
 | 
			
		||||
		buf = append(buf, host...)
 | 
			
		||||
	}
 | 
			
		||||
	buf = append(buf, byte(port>>8), byte(port))
 | 
			
		||||
 | 
			
		||||
	if _, err := conn.Write(buf); err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := io.ReadFull(conn, buf[:4]); err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failure := "unknown error"
 | 
			
		||||
	if int(buf[1]) < len(proxy_socks5Errors) {
 | 
			
		||||
		failure = proxy_socks5Errors[buf[1]]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(failure) > 0 {
 | 
			
		||||
		return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bytesToDiscard := 0
 | 
			
		||||
	switch buf[3] {
 | 
			
		||||
	case proxy_socks5IP4:
 | 
			
		||||
		bytesToDiscard = net.IPv4len
 | 
			
		||||
	case proxy_socks5IP6:
 | 
			
		||||
		bytesToDiscard = net.IPv6len
 | 
			
		||||
	case proxy_socks5Domain:
 | 
			
		||||
		_, err := io.ReadFull(conn, buf[:1])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		bytesToDiscard = int(buf[0])
 | 
			
		||||
	default:
 | 
			
		||||
		return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cap(buf) < bytesToDiscard {
 | 
			
		||||
		buf = make([]byte, bytesToDiscard)
 | 
			
		||||
	} else {
 | 
			
		||||
		buf = buf[:bytesToDiscard]
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := io.ReadFull(conn, buf); err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Also need to discard the port number
 | 
			
		||||
	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
 | 
			
		||||
		return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/labstack/echo/context.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -274,6 +274,13 @@ func (c *context) Param(name string) string {
 | 
			
		||||
			if n == name {
 | 
			
		||||
				return c.pvalues[i]
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Param name with aliases
 | 
			
		||||
			for _, p := range strings.Split(n, ",") {
 | 
			
		||||
				if p == name {
 | 
			
		||||
					return c.pvalues[i]
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
@@ -487,9 +494,14 @@ 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 NotFoundHandler(c)
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
@@ -498,7 +510,7 @@ func (c *context) File(file string) (err error) {
 | 
			
		||||
		file = filepath.Join(file, indexPage)
 | 
			
		||||
		f, err = os.Open(file)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return NotFoundHandler(c)
 | 
			
		||||
			return ErrNotFound
 | 
			
		||||
		}
 | 
			
		||||
		defer f.Close()
 | 
			
		||||
		if fi, err = f.Stat(); err != nil {
 | 
			
		||||
@@ -518,7 +530,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=%q", dispositionType, name))
 | 
			
		||||
	c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%s", dispositionType, name))
 | 
			
		||||
	c.File(file)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										145
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										145
									
								
								vendor/github.com/labstack/echo/echo.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -72,31 +72,29 @@ type (
 | 
			
		||||
		TLSServer        *http.Server
 | 
			
		||||
		Listener         net.Listener
 | 
			
		||||
		TLSListener      net.Listener
 | 
			
		||||
		AutoTLSManager   autocert.Manager
 | 
			
		||||
		DisableHTTP2     bool
 | 
			
		||||
		Debug            bool
 | 
			
		||||
		HideBanner       bool
 | 
			
		||||
		HidePort         bool
 | 
			
		||||
		HTTPErrorHandler HTTPErrorHandler
 | 
			
		||||
		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"`
 | 
			
		||||
		Name   string `json:"name"`
 | 
			
		||||
		Method  string `json:"method"`
 | 
			
		||||
		Path    string `json:"path"`
 | 
			
		||||
		Handler string `json:"handler"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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.
 | 
			
		||||
@@ -123,7 +121,7 @@ type (
 | 
			
		||||
 | 
			
		||||
	// i is the interface for Echo and Group.
 | 
			
		||||
	i interface {
 | 
			
		||||
		GET(string, HandlerFunc, ...MiddlewareFunc) *Route
 | 
			
		||||
		GET(string, HandlerFunc, ...MiddlewareFunc)
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +212,7 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	version = "3.2.6"
 | 
			
		||||
	version = "3.1.0"
 | 
			
		||||
	website = "https://echo.labstack.com"
 | 
			
		||||
	// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
 | 
			
		||||
	banner = `
 | 
			
		||||
@@ -284,7 +282,7 @@ func New() (e *Echo) {
 | 
			
		||||
	e.TLSServer.Handler = e
 | 
			
		||||
	e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
 | 
			
		||||
	e.Binder = &DefaultBinder{}
 | 
			
		||||
	e.Logger.SetLevel(log.ERROR)
 | 
			
		||||
	e.Logger.SetLevel(log.OFF)
 | 
			
		||||
	e.stdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
 | 
			
		||||
	e.pool.New = func() interface{} {
 | 
			
		||||
		return e.NewContext(nil, nil)
 | 
			
		||||
@@ -321,9 +319,6 @@ 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 {
 | 
			
		||||
@@ -333,19 +328,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
 | 
			
		||||
			err = c.NoContent(code)
 | 
			
		||||
			if err := c.NoContent(code); err != nil {
 | 
			
		||||
				goto ERROR
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			err = c.JSON(code, msg)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			e.Logger.Error(err)
 | 
			
		||||
			if err := c.JSON(code, msg); err != nil {
 | 
			
		||||
				goto ERROR
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
ERROR:
 | 
			
		||||
	e.Logger.Error(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pre adds middleware to the chain which is run before router.
 | 
			
		||||
@@ -360,114 +355,104 @@ 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) *Route {
 | 
			
		||||
	return e.Add(CONNECT, path, h, m...)
 | 
			
		||||
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(DELETE, path, h, m...)
 | 
			
		||||
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(GET, path, h, m...)
 | 
			
		||||
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(HEAD, path, h, m...)
 | 
			
		||||
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(OPTIONS, path, h, m...)
 | 
			
		||||
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(PATCH, path, h, m...)
 | 
			
		||||
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(POST, path, h, m...)
 | 
			
		||||
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(PUT, path, h, m...)
 | 
			
		||||
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return e.Add(TRACE, path, h, m...)
 | 
			
		||||
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) []*Route {
 | 
			
		||||
	routes := make([]*Route, len(methods))
 | 
			
		||||
	for i, m := range methods {
 | 
			
		||||
		routes[i] = e.Add(m, path, handler, middleware...)
 | 
			
		||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		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) []*Route {
 | 
			
		||||
	routes := make([]*Route, len(methods))
 | 
			
		||||
	for i, m := range methods {
 | 
			
		||||
		routes[i] = e.Add(m, path, handler, middleware...)
 | 
			
		||||
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		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) *Route {
 | 
			
		||||
func (e *Echo) Static(prefix, root string) {
 | 
			
		||||
	if root == "" {
 | 
			
		||||
		root = "." // For security we want to restrict to CWD.
 | 
			
		||||
	}
 | 
			
		||||
	return static(e, prefix, root)
 | 
			
		||||
	static(e, prefix, root)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func static(i i, prefix, root string) *Route {
 | 
			
		||||
func static(i i, prefix, root string) {
 | 
			
		||||
	h := func(c Context) error {
 | 
			
		||||
		p, err := PathUnescape(c.Param("*"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
 | 
			
		||||
		name := filepath.Join(root, path.Clean("/"+c.Param("*"))) // "/"+ for security
 | 
			
		||||
		return c.File(name)
 | 
			
		||||
	}
 | 
			
		||||
	i.GET(prefix, h)
 | 
			
		||||
	if prefix == "/" {
 | 
			
		||||
		return i.GET(prefix+"*", h)
 | 
			
		||||
		i.GET(prefix+"*", h)
 | 
			
		||||
	} else {
 | 
			
		||||
		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) *Route {
 | 
			
		||||
	return e.GET(path, func(c Context) error {
 | 
			
		||||
func (e *Echo) File(path, file string) {
 | 
			
		||||
	e.GET(path, func(c Context) error {
 | 
			
		||||
		return c.File(file)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	name := handlerName(handler)
 | 
			
		||||
	e.router.Add(method, path, func(c Context) error {
 | 
			
		||||
		h := handler
 | 
			
		||||
@@ -478,12 +463,11 @@ func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...Middl
 | 
			
		||||
		return h(c)
 | 
			
		||||
	})
 | 
			
		||||
	r := &Route{
 | 
			
		||||
		Method: method,
 | 
			
		||||
		Path:   path,
 | 
			
		||||
		Name:   name,
 | 
			
		||||
		Method:  method,
 | 
			
		||||
		Path:    path,
 | 
			
		||||
		Handler: name,
 | 
			
		||||
	}
 | 
			
		||||
	e.router.routes[method+path] = r
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Group creates a new router group with prefix and optional group-level middleware.
 | 
			
		||||
@@ -495,22 +479,12 @@ 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.Name == name {
 | 
			
		||||
		if r.Handler == name {
 | 
			
		||||
			for i, l := 0, len(r.Path); i < l; i++ {
 | 
			
		||||
				if r.Path[i] == ':' && n < ln {
 | 
			
		||||
					for ; i < l && r.Path[i] != '/'; i++ {
 | 
			
		||||
@@ -528,6 +502,11 @@ func (e *Echo) Reverse(name string, 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{}
 | 
			
		||||
@@ -645,7 +624,7 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !e.HidePort {
 | 
			
		||||
		if !e.HideBanner {
 | 
			
		||||
			e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
 | 
			
		||||
		}
 | 
			
		||||
		return s.Serve(e.Listener)
 | 
			
		||||
@@ -657,7 +636,7 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
 | 
			
		||||
		}
 | 
			
		||||
		e.TLSListener = tls.NewListener(l, s.TLSConfig)
 | 
			
		||||
	}
 | 
			
		||||
	if !e.HidePort {
 | 
			
		||||
	if !e.HideBanner {
 | 
			
		||||
		e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
 | 
			
		||||
	}
 | 
			
		||||
	return s.Serve(e.TLSListener)
 | 
			
		||||
@@ -674,7 +653,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=%v", he.Code, he.Message)
 | 
			
		||||
	return fmt.Sprintf("code=%d, message=%s", he.Code, he.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/labstack/echo/group.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -20,74 +20,68 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
 | 
			
		||||
	g.middleware = append(g.middleware, middleware...)
 | 
			
		||||
	// 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.
 | 
			
		||||
	for _, p := range []string{"", "/*"} {
 | 
			
		||||
		g.echo.Any(path.Clean(g.prefix+p), func(c Context) error {
 | 
			
		||||
			return NotFoundHandler(c)
 | 
			
		||||
		}, g.middleware...)
 | 
			
		||||
	}
 | 
			
		||||
	g.echo.Any(path.Clean(g.prefix+"/*"), func(c Context) error {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
	}, g.middleware...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
 | 
			
		||||
	return g.Add(CONNECT, path, h, m...)
 | 
			
		||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(DELETE, path, h, m...)
 | 
			
		||||
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(GET, path, h, m...)
 | 
			
		||||
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(HEAD, path, h, m...)
 | 
			
		||||
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(OPTIONS, path, h, m...)
 | 
			
		||||
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(PATCH, path, h, m...)
 | 
			
		||||
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(POST, path, h, m...)
 | 
			
		||||
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(PUT, path, h, m...)
 | 
			
		||||
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) *Route {
 | 
			
		||||
	return g.Add(TRACE, path, h, m...)
 | 
			
		||||
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
 | 
			
		||||
	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) []*Route {
 | 
			
		||||
	routes := make([]*Route, len(methods))
 | 
			
		||||
	for i, m := range methods {
 | 
			
		||||
		routes[i] = g.Add(m, path, handler, middleware...)
 | 
			
		||||
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		g.add(m, path, handler, middleware...)
 | 
			
		||||
	}
 | 
			
		||||
	return routes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match implements `Echo#Match()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
 | 
			
		||||
	routes := make([]*Route, len(methods))
 | 
			
		||||
	for i, m := range methods {
 | 
			
		||||
		routes[i] = g.Add(m, path, handler, middleware...)
 | 
			
		||||
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	for _, m := range methods {
 | 
			
		||||
		g.add(m, path, handler, middleware...)
 | 
			
		||||
	}
 | 
			
		||||
	return routes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Group creates a new sub-group with prefix and optional sub-group-level middleware.
 | 
			
		||||
@@ -108,13 +102,12 @@ func (g *Group) File(path, file string) {
 | 
			
		||||
	g.echo.File(g.prefix+path, file)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add implements `Echo#Add()` for sub-routes within the Group.
 | 
			
		||||
func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
 | 
			
		||||
func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
 | 
			
		||||
	// 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...)
 | 
			
		||||
	return g.echo.Add(method, g.prefix+path, handler, m...)
 | 
			
		||||
	g.echo.add(method, g.prefix+path, handler, m...)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/labstack/echo/middleware/basic_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,7 +3,6 @@ package middleware
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
@@ -28,7 +27,7 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	basic        = "basic"
 | 
			
		||||
	basic        = "Basic"
 | 
			
		||||
	defaultRealm = "Restricted"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +54,7 @@ func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
 | 
			
		||||
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Validator == nil {
 | 
			
		||||
		panic("echo: basic-auth middleware requires a validator function")
 | 
			
		||||
		panic("basic-auth middleware requires a validator function")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultBasicAuthConfig.Skipper
 | 
			
		||||
@@ -73,7 +72,7 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
			auth := c.Request().Header.Get(echo.HeaderAuthorization)
 | 
			
		||||
			l := len(basic)
 | 
			
		||||
 | 
			
		||||
			if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
 | 
			
		||||
			if len(auth) > l+1 && auth[:l] == basic {
 | 
			
		||||
				b, err := base64.StdEncoding.DecodeString(auth[l+1:])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
@@ -88,7 +87,6 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
						} else if valid {
 | 
			
		||||
							return next(c)
 | 
			
		||||
						}
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/labstack/echo/middleware/body_dump.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,112 +0,0 @@
 | 
			
		||||
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 BodyDump 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()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/labstack/echo/middleware/body_limit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ type (
 | 
			
		||||
 | 
			
		||||
		// Maximum allowed size for a request body, it can be specified
 | 
			
		||||
		// as `4x` or `4xB`, where x is one of the multiple from K, M, G, T or P.
 | 
			
		||||
		Limit string `yaml:"limit"`
 | 
			
		||||
		Limit string `json:"limit"`
 | 
			
		||||
		limit int64
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +30,7 @@ type (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultBodyLimitConfig is the default BodyLimit middleware config.
 | 
			
		||||
	// DefaultBodyLimitConfig is the default Gzip 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("echo: invalid body-limit=%s", config.Limit))
 | 
			
		||||
		panic(fmt.Errorf("invalid body-limit=%s", config.Limit))
 | 
			
		||||
	}
 | 
			
		||||
	config.limit = limit
 | 
			
		||||
	pool := limitedReaderPool(config)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/labstack/echo/middleware/compress.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -20,7 +20,7 @@ type (
 | 
			
		||||
 | 
			
		||||
		// Gzip compression level.
 | 
			
		||||
		// Optional. Default value -1.
 | 
			
		||||
		Level int `yaml:"level"`
 | 
			
		||||
		Level int `json:"level"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gzipResponseWriter struct {
 | 
			
		||||
@@ -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().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
 | 
			
		||||
				res.Header().Add(echo.HeaderContentEncoding, gzipScheme) // Issue #806
 | 
			
		||||
				rw := res.Writer
 | 
			
		||||
				w, err := gzip.NewWriterLevel(rw, config.Level)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -98,7 +98,6 @@ 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/middleware/cors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/middleware/cors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,34 +16,34 @@ type (
 | 
			
		||||
 | 
			
		||||
		// AllowOrigin defines a list of origins that may access the resource.
 | 
			
		||||
		// Optional. Default value []string{"*"}.
 | 
			
		||||
		AllowOrigins []string `yaml:"allow_origins"`
 | 
			
		||||
		AllowOrigins []string `json:"allow_origins"`
 | 
			
		||||
 | 
			
		||||
		// AllowMethods defines a list methods allowed when accessing the resource.
 | 
			
		||||
		// This is used in response to a preflight request.
 | 
			
		||||
		// Optional. Default value DefaultCORSConfig.AllowMethods.
 | 
			
		||||
		AllowMethods []string `yaml:"allow_methods"`
 | 
			
		||||
		AllowMethods []string `json:"allow_methods"`
 | 
			
		||||
 | 
			
		||||
		// AllowHeaders defines a list of request headers that can be used when
 | 
			
		||||
		// making the actual request. This in response to a preflight request.
 | 
			
		||||
		// Optional. Default value []string{}.
 | 
			
		||||
		AllowHeaders []string `yaml:"allow_headers"`
 | 
			
		||||
		AllowHeaders []string `json:"allow_headers"`
 | 
			
		||||
 | 
			
		||||
		// AllowCredentials indicates whether or not the response to the request
 | 
			
		||||
		// can be exposed when the credentials flag is true. When used as part of
 | 
			
		||||
		// a response to a preflight request, this indicates whether or not the
 | 
			
		||||
		// actual request can be made using credentials.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		AllowCredentials bool `yaml:"allow_credentials"`
 | 
			
		||||
		AllowCredentials bool `json:"allow_credentials"`
 | 
			
		||||
 | 
			
		||||
		// ExposeHeaders defines a whitelist headers that clients are allowed to
 | 
			
		||||
		// access.
 | 
			
		||||
		// Optional. Default value []string{}.
 | 
			
		||||
		ExposeHeaders []string `yaml:"expose_headers"`
 | 
			
		||||
		ExposeHeaders []string `json:"expose_headers"`
 | 
			
		||||
 | 
			
		||||
		// MaxAge indicates how long (in seconds) the results of a preflight request
 | 
			
		||||
		// can be cached.
 | 
			
		||||
		// Optional. Default value 0.
 | 
			
		||||
		MaxAge int `yaml:"max_age"`
 | 
			
		||||
		MaxAge int `json:"max_age"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								vendor/github.com/labstack/echo/middleware/csrf.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/labstack/echo/middleware/csrf.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ type (
 | 
			
		||||
		Skipper Skipper
 | 
			
		||||
 | 
			
		||||
		// TokenLength is the length of the generated token.
 | 
			
		||||
		TokenLength uint8 `yaml:"token_length"`
 | 
			
		||||
		TokenLength uint8 `json:"token_length"`
 | 
			
		||||
		// Optional. Default value 32.
 | 
			
		||||
 | 
			
		||||
		// TokenLookup is a string in the form of "<source>:<key>" that is used
 | 
			
		||||
@@ -28,35 +28,35 @@ type (
 | 
			
		||||
		// - "header:<name>"
 | 
			
		||||
		// - "form:<name>"
 | 
			
		||||
		// - "query:<name>"
 | 
			
		||||
		TokenLookup string `yaml:"token_lookup"`
 | 
			
		||||
		TokenLookup string `json:"token_lookup"`
 | 
			
		||||
 | 
			
		||||
		// Context key to store generated CSRF token into context.
 | 
			
		||||
		// Optional. Default value "csrf".
 | 
			
		||||
		ContextKey string `yaml:"context_key"`
 | 
			
		||||
		ContextKey string `json:"context_key"`
 | 
			
		||||
 | 
			
		||||
		// Name of the CSRF cookie. This cookie will store CSRF token.
 | 
			
		||||
		// Optional. Default value "csrf".
 | 
			
		||||
		CookieName string `yaml:"cookie_name"`
 | 
			
		||||
		CookieName string `json:"cookie_name"`
 | 
			
		||||
 | 
			
		||||
		// Domain of the CSRF cookie.
 | 
			
		||||
		// Optional. Default value none.
 | 
			
		||||
		CookieDomain string `yaml:"cookie_domain"`
 | 
			
		||||
		CookieDomain string `json:"cookie_domain"`
 | 
			
		||||
 | 
			
		||||
		// Path of the CSRF cookie.
 | 
			
		||||
		// Optional. Default value none.
 | 
			
		||||
		CookiePath string `yaml:"cookie_path"`
 | 
			
		||||
		CookiePath string `json:"cookie_path"`
 | 
			
		||||
 | 
			
		||||
		// Max age (in seconds) of the CSRF cookie.
 | 
			
		||||
		// Optional. Default value 86400 (24hr).
 | 
			
		||||
		CookieMaxAge int `yaml:"cookie_max_age"`
 | 
			
		||||
		CookieMaxAge int `json:"cookie_max_age"`
 | 
			
		||||
 | 
			
		||||
		// Indicates if CSRF cookie is secure.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		CookieSecure bool `yaml:"cookie_secure"`
 | 
			
		||||
		CookieSecure bool `json:"cookie_secure"`
 | 
			
		||||
 | 
			
		||||
		// Indicates if CSRF cookie is HTTP only.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		CookieHTTPOnly bool `yaml:"cookie_http_only"`
 | 
			
		||||
		CookieHTTPOnly bool `json:"cookie_http_only"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// csrfTokenExtractor defines a function that takes `echo.Context` and returns
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/labstack/echo/middleware/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -56,12 +57,6 @@ 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{
 | 
			
		||||
@@ -82,7 +77,7 @@ var (
 | 
			
		||||
//
 | 
			
		||||
// See: https://jwt.io/introduction
 | 
			
		||||
// See `JWTConfig.TokenLookup`
 | 
			
		||||
func JWT(key interface{}) echo.MiddlewareFunc {
 | 
			
		||||
func JWT(key []byte) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultJWTConfig
 | 
			
		||||
	c.SigningKey = key
 | 
			
		||||
	return JWTWithConfig(c)
 | 
			
		||||
@@ -139,15 +134,14 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
			auth, err := extractor(c)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
				return echo.NewHTTPError(http.StatusBadRequest, err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			token := new(jwt.Token)
 | 
			
		||||
			// Issue #647, #656
 | 
			
		||||
			if _, ok := config.Claims.(jwt.MapClaims); ok {
 | 
			
		||||
				token, err = jwt.Parse(auth, config.keyFunc)
 | 
			
		||||
			} else {
 | 
			
		||||
				t := reflect.ValueOf(config.Claims).Type().Elem()
 | 
			
		||||
				claims := reflect.New(t).Interface().(jwt.Claims)
 | 
			
		||||
				claims := reflect.ValueOf(config.Claims).Interface().(jwt.Claims)
 | 
			
		||||
				token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
 | 
			
		||||
			}
 | 
			
		||||
			if err == nil && token.Valid {
 | 
			
		||||
@@ -155,11 +149,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
 | 
			
		||||
				c.Set(config.ContextKey, token)
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
			return &echo.HTTPError{
 | 
			
		||||
				Code:    ErrJWTInvalid.Code,
 | 
			
		||||
				Message: ErrJWTInvalid.Message,
 | 
			
		||||
				Inner:   err,
 | 
			
		||||
			}
 | 
			
		||||
			return echo.ErrUnauthorized
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -172,7 +162,7 @@ func jwtFromHeader(header string, authScheme string) jwtExtractor {
 | 
			
		||||
		if len(auth) > l+1 && auth[:l] == authScheme {
 | 
			
		||||
			return auth[l+1:], nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", ErrJWTMissing
 | 
			
		||||
		return "", errors.New("Missing or invalid jwt in the request header")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -181,7 +171,7 @@ func jwtFromQuery(param string) jwtExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		token := c.QueryParam(param)
 | 
			
		||||
		if token == "" {
 | 
			
		||||
			return "", ErrJWTMissing
 | 
			
		||||
			return "", errors.New("Missing jwt in the query string")
 | 
			
		||||
		}
 | 
			
		||||
		return token, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -192,7 +182,7 @@ func jwtFromCookie(name string) jwtExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		cookie, err := c.Cookie(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", ErrJWTMissing
 | 
			
		||||
			return "", errors.New("Missing jwt in the cookie")
 | 
			
		||||
		}
 | 
			
		||||
		return cookie.Value, nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/labstack/echo/middleware/key_auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -20,8 +20,7 @@ type (
 | 
			
		||||
		// Possible values:
 | 
			
		||||
		// - "header:<name>"
 | 
			
		||||
		// - "query:<name>"
 | 
			
		||||
		// - "form:<name>"
 | 
			
		||||
		KeyLookup string `yaml:"key_lookup"`
 | 
			
		||||
		KeyLookup string `json:"key_lookup"`
 | 
			
		||||
 | 
			
		||||
		// AuthScheme to be used in the Authorization header.
 | 
			
		||||
		// Optional. Default value "Bearer".
 | 
			
		||||
@@ -73,7 +72,7 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
		config.KeyLookup = DefaultKeyAuthConfig.KeyLookup
 | 
			
		||||
	}
 | 
			
		||||
	if config.Validator == nil {
 | 
			
		||||
		panic("echo: key-auth middleware requires a validator function")
 | 
			
		||||
		panic("key-auth middleware requires a validator function")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Initialize
 | 
			
		||||
@@ -82,8 +81,6 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
 | 
			
		||||
	switch parts[0] {
 | 
			
		||||
	case "query":
 | 
			
		||||
		extractor = keyFromQuery(parts[1])
 | 
			
		||||
	case "form":
 | 
			
		||||
		extractor = keyFromForm(parts[1])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
@@ -137,14 +134,3 @@ func keyFromQuery(param string) keyExtractor {
 | 
			
		||||
		return key, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// keyFromForm returns a `keyExtractor` that extracts key from the form.
 | 
			
		||||
func keyFromForm(param string) keyExtractor {
 | 
			
		||||
	return func(c echo.Context) (string, error) {
 | 
			
		||||
		key := c.FormValue(param)
 | 
			
		||||
		if key == "" {
 | 
			
		||||
			return "", errors.New("Missing key in the form")
 | 
			
		||||
		}
 | 
			
		||||
		return key, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/labstack/echo/middleware/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/labstack/echo/middleware/logger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -26,7 +26,6 @@ type (
 | 
			
		||||
		// - time_unix_nano
 | 
			
		||||
		// - time_rfc3339
 | 
			
		||||
		// - time_rfc3339_nano
 | 
			
		||||
		// - time_custom
 | 
			
		||||
		// - id (Request ID)
 | 
			
		||||
		// - remote_ip
 | 
			
		||||
		// - uri
 | 
			
		||||
@@ -47,10 +46,7 @@ type (
 | 
			
		||||
		// Example "${remote_ip} ${status}"
 | 
			
		||||
		//
 | 
			
		||||
		// Optional. Default value DefaultLoggerConfig.Format.
 | 
			
		||||
		Format                string `yaml:"format"`
 | 
			
		||||
 | 
			
		||||
		// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
 | 
			
		||||
		CustomTimeFormat string `yaml:"custom_time_format"`
 | 
			
		||||
		Format string `json:"format"`
 | 
			
		||||
 | 
			
		||||
		// Output is a writer where logs in JSON format are written.
 | 
			
		||||
		// Optional. Default value os.Stdout.
 | 
			
		||||
@@ -70,7 +66,6 @@ var (
 | 
			
		||||
			`"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` +
 | 
			
		||||
			`"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
 | 
			
		||||
			`"bytes_out":${bytes_out}}` + "\n",
 | 
			
		||||
		CustomTimeFormat:"2006-01-02 15:04:05.00000",
 | 
			
		||||
		Output:  os.Stdout,
 | 
			
		||||
		colorer: color.New(),
 | 
			
		||||
	}
 | 
			
		||||
@@ -131,8 +126,6 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
 | 
			
		||||
					return buf.WriteString(time.Now().Format(time.RFC3339))
 | 
			
		||||
				case "time_rfc3339_nano":
 | 
			
		||||
					return buf.WriteString(time.Now().Format(time.RFC3339Nano))
 | 
			
		||||
				case "time_custom":
 | 
			
		||||
					return buf.WriteString(time.Now().Format(config.CustomTimeFormat))
 | 
			
		||||
				case "id":
 | 
			
		||||
					id := req.Header.Get(echo.HeaderXRequestID)
 | 
			
		||||
					if id == "" {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/labstack/echo/middleware/middleware.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/labstack/echo/middleware/middleware.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,12 +1,6 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
import "github.com/labstack/echo"
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// Skipper defines a function to skip middleware. Returning true skips processing
 | 
			
		||||
@@ -14,21 +8,6 @@ type (
 | 
			
		||||
	Skipper func(c echo.Context) bool
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
 | 
			
		||||
	groups := pattern.FindAllStringSubmatch(input, -1)
 | 
			
		||||
	if groups == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	values := groups[0][1:]
 | 
			
		||||
	replace := make([]string, 2*len(values))
 | 
			
		||||
	for i, v := range values {
 | 
			
		||||
		j := 2 * i
 | 
			
		||||
		replace[j] = "$" + strconv.Itoa(i+1)
 | 
			
		||||
		replace[j+1] = v
 | 
			
		||||
	}
 | 
			
		||||
	return strings.NewReplacer(replace...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultSkipper returns false which processes the middleware.
 | 
			
		||||
func DefaultSkipper(echo.Context) bool {
 | 
			
		||||
	return false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										166
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										166
									
								
								vendor/github.com/labstack/echo/middleware/proxy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
@@ -8,9 +9,6 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httputil"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -27,56 +25,33 @@ type (
 | 
			
		||||
 | 
			
		||||
		// Balancer defines a load balancing technique.
 | 
			
		||||
		// Required.
 | 
			
		||||
		// Possible values:
 | 
			
		||||
		// - RandomBalancer
 | 
			
		||||
		// - RoundRobinBalancer
 | 
			
		||||
		Balancer ProxyBalancer
 | 
			
		||||
 | 
			
		||||
		// Rewrite defines URL path rewrite rules. The values captured in asterisk can be
 | 
			
		||||
		// retrieved by index e.g. $1, $2 and so on.
 | 
			
		||||
		// Examples:
 | 
			
		||||
		// "/old":              "/new",
 | 
			
		||||
		// "/api/*":            "/$1",
 | 
			
		||||
		// "/js/*":             "/public/javascripts/$1",
 | 
			
		||||
		// "/users/*/orders/*": "/user/$1/order/$2",
 | 
			
		||||
		Rewrite map[string]string
 | 
			
		||||
 | 
			
		||||
		rewriteRegex map[*regexp.Regexp]string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ProxyTarget defines the upstream target.
 | 
			
		||||
	ProxyTarget struct {
 | 
			
		||||
		Name string
 | 
			
		||||
		URL  *url.URL
 | 
			
		||||
		URL *url.URL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// RandomBalancer implements a random load balancing technique.
 | 
			
		||||
	RandomBalancer struct {
 | 
			
		||||
		Targets []*ProxyTarget
 | 
			
		||||
		random  *rand.Rand
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// RoundRobinBalancer implements a round-robin load balancing technique.
 | 
			
		||||
	RoundRobinBalancer struct {
 | 
			
		||||
		Targets []*ProxyTarget
 | 
			
		||||
		i       uint32
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ProxyBalancer defines an interface to implement a load balancing technique.
 | 
			
		||||
	ProxyBalancer interface {
 | 
			
		||||
		AddTarget(*ProxyTarget) bool
 | 
			
		||||
		RemoveTarget(string) bool
 | 
			
		||||
		Next() *ProxyTarget
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	commonBalancer struct {
 | 
			
		||||
		targets []*ProxyTarget
 | 
			
		||||
		mutex   sync.RWMutex
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// RandomBalancer implements a random load balancing technique.
 | 
			
		||||
	randomBalancer struct {
 | 
			
		||||
		*commonBalancer
 | 
			
		||||
		random *rand.Rand
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// RoundRobinBalancer implements a round-robin load balancing technique.
 | 
			
		||||
	roundRobinBalancer struct {
 | 
			
		||||
		*commonBalancer
 | 
			
		||||
		i uint32
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultProxyConfig is the default Proxy middleware config.
 | 
			
		||||
	DefaultProxyConfig = ProxyConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func proxyHTTP(t *ProxyTarget) http.Handler {
 | 
			
		||||
@@ -85,25 +60,29 @@ func proxyHTTP(t *ProxyTarget) http.Handler {
 | 
			
		||||
 | 
			
		||||
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		in, _, err := c.Response().Hijack()
 | 
			
		||||
		h, ok := w.(http.Hijacker)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			c.Error(errors.New("proxy raw, not a hijacker"))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		in, _, err := h.Hijack()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err))
 | 
			
		||||
			c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", r.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", t.URL, err))
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", r.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 header copy error=%v, url=%s", t.URL, err))
 | 
			
		||||
			he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request copy error=%v, url=%s", r.URL, err))
 | 
			
		||||
			c.Error(he)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -118,81 +97,29 @@ 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, copy body error=%v, url=%s", t.URL, err)
 | 
			
		||||
			c.Logger().Errorf("proxy raw, error=%v, url=%s", r.URL, err)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRandomBalancer returns a random proxy balancer.
 | 
			
		||||
func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer {
 | 
			
		||||
	b := &randomBalancer{commonBalancer: new(commonBalancer)}
 | 
			
		||||
	b.targets = targets
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRoundRobinBalancer returns a round-robin proxy balancer.
 | 
			
		||||
func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer {
 | 
			
		||||
	b := &roundRobinBalancer{commonBalancer: new(commonBalancer)}
 | 
			
		||||
	b.targets = targets
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddTarget adds an upstream target to the list.
 | 
			
		||||
func (b *commonBalancer) AddTarget(target *ProxyTarget) bool {
 | 
			
		||||
	for _, t := range b.targets {
 | 
			
		||||
		if t.Name == target.Name {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b.mutex.Lock()
 | 
			
		||||
	defer b.mutex.Unlock()
 | 
			
		||||
	b.targets = append(b.targets, target)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveTarget removes an upstream target from the list.
 | 
			
		||||
func (b *commonBalancer) RemoveTarget(name string) bool {
 | 
			
		||||
	b.mutex.Lock()
 | 
			
		||||
	defer b.mutex.Unlock()
 | 
			
		||||
	for i, t := range b.targets {
 | 
			
		||||
		if t.Name == name {
 | 
			
		||||
			b.targets = append(b.targets[:i], b.targets[i+1:]...)
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next randomly returns an upstream target.
 | 
			
		||||
func (b *randomBalancer) Next() *ProxyTarget {
 | 
			
		||||
	if b.random == nil {
 | 
			
		||||
		b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
 | 
			
		||||
func (r *RandomBalancer) Next() *ProxyTarget {
 | 
			
		||||
	if r.random == nil {
 | 
			
		||||
		r.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
 | 
			
		||||
	}
 | 
			
		||||
	b.mutex.RLock()
 | 
			
		||||
	defer b.mutex.RUnlock()
 | 
			
		||||
	return b.targets[b.random.Intn(len(b.targets))]
 | 
			
		||||
	return r.Targets[r.random.Intn(len(r.Targets))]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next returns an upstream target using round-robin technique.
 | 
			
		||||
func (b *roundRobinBalancer) Next() *ProxyTarget {
 | 
			
		||||
	b.i = b.i % uint32(len(b.targets))
 | 
			
		||||
	t := b.targets[b.i]
 | 
			
		||||
	atomic.AddUint32(&b.i, 1)
 | 
			
		||||
func (r *RoundRobinBalancer) Next() *ProxyTarget {
 | 
			
		||||
	r.i = r.i % uint32(len(r.Targets))
 | 
			
		||||
	t := r.Targets[r.i]
 | 
			
		||||
	atomic.AddUint32(&r.i, 1)
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
// Proxy returns an HTTP/WebSocket reverse proxy middleware.
 | 
			
		||||
func Proxy(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultLoggerConfig.Skipper
 | 
			
		||||
@@ -200,32 +127,13 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
 | 
			
		||||
	if config.Balancer == nil {
 | 
			
		||||
		panic("echo: proxy middleware requires balancer")
 | 
			
		||||
	}
 | 
			
		||||
	config.rewriteRegex = map[*regexp.Regexp]string{}
 | 
			
		||||
 | 
			
		||||
	// Initialize
 | 
			
		||||
	for k, v := range config.Rewrite {
 | 
			
		||||
		k = strings.Replace(k, "*", "(\\S*)", -1)
 | 
			
		||||
		config.rewriteRegex[regexp.MustCompile(k)] = v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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()
 | 
			
		||||
 | 
			
		||||
			// Rewrite
 | 
			
		||||
			for k, v := range config.rewriteRegex {
 | 
			
		||||
				replacer := captureTokens(k, req.URL.Path)
 | 
			
		||||
				if replacer != nil {
 | 
			
		||||
					req.URL.Path = replacer.Replace(v)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Fix header
 | 
			
		||||
			if req.Header.Get(echo.HeaderXRealIP) == "" {
 | 
			
		||||
				req.Header.Set(echo.HeaderXRealIP, c.RealIP())
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/labstack/echo/middleware/recover.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
	"github.com/labstack/gommon/color"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
@@ -15,16 +16,16 @@ type (
 | 
			
		||||
 | 
			
		||||
		// Size of the stack to be printed.
 | 
			
		||||
		// Optional. Default value 4KB.
 | 
			
		||||
		StackSize int `yaml:"stack_size"`
 | 
			
		||||
		StackSize int `json:"stack_size"`
 | 
			
		||||
 | 
			
		||||
		// DisableStackAll disables formatting stack traces of all other goroutines
 | 
			
		||||
		// into buffer after the trace for the current goroutine.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		DisableStackAll bool `yaml:"disable_stack_all"`
 | 
			
		||||
		DisableStackAll bool `json:"disable_stack_all"`
 | 
			
		||||
 | 
			
		||||
		// DisablePrintStack disables printing stack trace.
 | 
			
		||||
		// Optional. Default value as false.
 | 
			
		||||
		DisablePrintStack bool `yaml:"disable_print_stack"`
 | 
			
		||||
		DisablePrintStack bool `json:"disable_print_stack"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -63,14 +64,17 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if r := recover(); r != nil {
 | 
			
		||||
					err, ok := r.(error)
 | 
			
		||||
					if !ok {
 | 
			
		||||
					var err error
 | 
			
		||||
					switch r := r.(type) {
 | 
			
		||||
					case error:
 | 
			
		||||
						err = r
 | 
			
		||||
					default:
 | 
			
		||||
						err = fmt.Errorf("%v", r)
 | 
			
		||||
					}
 | 
			
		||||
					stack := make([]byte, config.StackSize)
 | 
			
		||||
					length := runtime.Stack(stack, !config.DisableStackAll)
 | 
			
		||||
					if !config.DisablePrintStack {
 | 
			
		||||
						c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length])
 | 
			
		||||
						c.Logger().Printf("[%s] %s %s\n", color.Red("PANIC RECOVER"), err, stack[:length])
 | 
			
		||||
					}
 | 
			
		||||
					c.Error(err)
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										284
									
								
								vendor/github.com/labstack/echo/middleware/redirect.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										284
									
								
								vendor/github.com/labstack/echo/middleware/redirect.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,28 +6,29 @@ import (
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RedirectConfig defines the config for Redirect middleware.
 | 
			
		||||
type RedirectConfig struct {
 | 
			
		||||
	// Skipper defines a function to skip middleware.
 | 
			
		||||
	Skipper
 | 
			
		||||
type (
 | 
			
		||||
	// RedirectConfig defines the config for Redirect middleware.
 | 
			
		||||
	RedirectConfig struct {
 | 
			
		||||
		// Skipper defines a function to skip middleware.
 | 
			
		||||
		Skipper Skipper
 | 
			
		||||
 | 
			
		||||
	// Status code to be used when redirecting the request.
 | 
			
		||||
	// Optional. Default value http.StatusMovedPermanently.
 | 
			
		||||
	Code int `yaml:"code"`
 | 
			
		||||
}
 | 
			
		||||
		// Status code to be used when redirecting the request.
 | 
			
		||||
		// Optional. Default value http.StatusMovedPermanently.
 | 
			
		||||
		Code int `json:"code"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// redirectLogic represents a function that given a scheme, host and uri
 | 
			
		||||
// can both: 1) determine if redirect is needed (will set ok accordingly) and
 | 
			
		||||
// 2) return the appropriate redirect url.
 | 
			
		||||
type redirectLogic func(scheme, host, uri string) (ok bool, url string)
 | 
			
		||||
const (
 | 
			
		||||
	www = "www"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const www = "www"
 | 
			
		||||
 | 
			
		||||
// DefaultRedirectConfig is the default Redirect middleware config.
 | 
			
		||||
var DefaultRedirectConfig = RedirectConfig{
 | 
			
		||||
	Skipper: DefaultSkipper,
 | 
			
		||||
	Code:    http.StatusMovedPermanently,
 | 
			
		||||
}
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultRedirectConfig is the default Redirect middleware config.
 | 
			
		||||
	DefaultRedirectConfig = RedirectConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
		Code:    http.StatusMovedPermanently,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HTTPSRedirect redirects http requests to https.
 | 
			
		||||
// For example, http://labstack.com will be redirect to https://labstack.com.
 | 
			
		||||
@@ -40,94 +41,7 @@ func HTTPSRedirect() echo.MiddlewareFunc {
 | 
			
		||||
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `HTTPSRedirect()`.
 | 
			
		||||
func HTTPSRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
 | 
			
		||||
		if ok = scheme != "https"; ok {
 | 
			
		||||
			url = "https://" + host + uri
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSWWWRedirect redirects http requests to https www.
 | 
			
		||||
// For example, http://labstack.com will be redirect to https://www.labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(HTTPSWWWRedirect())`
 | 
			
		||||
func HTTPSWWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return HTTPSWWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `HTTPSWWWRedirect()`.
 | 
			
		||||
func HTTPSWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
 | 
			
		||||
		if ok = scheme != "https" && host[:3] != www; ok {
 | 
			
		||||
			url = "https://www." + host + uri
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSNonWWWRedirect redirects http requests to https non www.
 | 
			
		||||
// For example, http://www.labstack.com will be redirect to https://labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(HTTPSNonWWWRedirect())`
 | 
			
		||||
func HTTPSNonWWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return HTTPSNonWWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `HTTPSNonWWWRedirect()`.
 | 
			
		||||
func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
 | 
			
		||||
		if ok = scheme != "https"; ok {
 | 
			
		||||
			if host[:3] == www {
 | 
			
		||||
				host = host[4:]
 | 
			
		||||
			}
 | 
			
		||||
			url = "https://" + host + uri
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WWWRedirect redirects non www requests to www.
 | 
			
		||||
// For example, http://labstack.com will be redirect to http://www.labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(WWWRedirect())`
 | 
			
		||||
func WWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return WWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `WWWRedirect()`.
 | 
			
		||||
func WWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
 | 
			
		||||
		if ok = host[:3] != www; ok {
 | 
			
		||||
			url = scheme + "://www." + host + uri
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NonWWWRedirect redirects www requests to non www.
 | 
			
		||||
// For example, http://www.labstack.com will be redirect to http://labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(NonWWWRedirect())`
 | 
			
		||||
func NonWWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return NonWWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `NonWWWRedirect()`.
 | 
			
		||||
func NonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
 | 
			
		||||
		if ok = host[:3] == www; ok {
 | 
			
		||||
			url = scheme + "://" + host[4:] + uri
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func redirect(config RedirectConfig, cb redirectLogic) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultTrailingSlashConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
@@ -141,12 +55,160 @@ func redirect(config RedirectConfig, cb redirectLogic) echo.MiddlewareFunc {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req, scheme := c.Request(), c.Scheme()
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			host := req.Host
 | 
			
		||||
			if ok, url := cb(scheme, host, req.RequestURI); ok {
 | 
			
		||||
				return c.Redirect(config.Code, url)
 | 
			
		||||
			uri := req.RequestURI
 | 
			
		||||
			if !c.IsTLS() {
 | 
			
		||||
				return c.Redirect(config.Code, "https://"+host+uri)
 | 
			
		||||
			}
 | 
			
		||||
			return next(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSWWWRedirect redirects http requests to https www.
 | 
			
		||||
// For example, http://labstack.com will be redirect to https://www.labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(HTTPSWWWRedirect())`
 | 
			
		||||
func HTTPSWWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return HTTPSWWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `HTTPSWWWRedirect()`.
 | 
			
		||||
func HTTPSWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultTrailingSlashConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
	if config.Code == 0 {
 | 
			
		||||
		config.Code = DefaultRedirectConfig.Code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			host := req.Host
 | 
			
		||||
			uri := req.RequestURI
 | 
			
		||||
			if !c.IsTLS() && host[:3] != www {
 | 
			
		||||
				return c.Redirect(config.Code, "https://www."+host+uri)
 | 
			
		||||
			}
 | 
			
		||||
			return next(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSNonWWWRedirect redirects http requests to https non www.
 | 
			
		||||
// For example, http://www.labstack.com will be redirect to https://labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(HTTPSNonWWWRedirect())`
 | 
			
		||||
func HTTPSNonWWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return HTTPSNonWWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `HTTPSNonWWWRedirect()`.
 | 
			
		||||
func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultTrailingSlashConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
	if config.Code == 0 {
 | 
			
		||||
		config.Code = DefaultRedirectConfig.Code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			host := req.Host
 | 
			
		||||
			uri := req.RequestURI
 | 
			
		||||
			if !c.IsTLS() {
 | 
			
		||||
				if host[:3] == www {
 | 
			
		||||
					return c.Redirect(config.Code, "https://"+host[4:]+uri)
 | 
			
		||||
				}
 | 
			
		||||
				return c.Redirect(config.Code, "https://"+host+uri)
 | 
			
		||||
			}
 | 
			
		||||
			return next(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WWWRedirect redirects non www requests to www.
 | 
			
		||||
// For example, http://labstack.com will be redirect to http://www.labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(WWWRedirect())`
 | 
			
		||||
func WWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return WWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `WWWRedirect()`.
 | 
			
		||||
func WWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultTrailingSlashConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
	if config.Code == 0 {
 | 
			
		||||
		config.Code = DefaultRedirectConfig.Code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			scheme := c.Scheme()
 | 
			
		||||
			host := req.Host
 | 
			
		||||
			if host[:3] != www {
 | 
			
		||||
				uri := req.RequestURI
 | 
			
		||||
				return c.Redirect(config.Code, scheme+"://www."+host+uri)
 | 
			
		||||
			}
 | 
			
		||||
			return next(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NonWWWRedirect redirects www requests to non www.
 | 
			
		||||
// For example, http://www.labstack.com will be redirect to http://labstack.com.
 | 
			
		||||
//
 | 
			
		||||
// Usage `Echo#Pre(NonWWWRedirect())`
 | 
			
		||||
func NonWWWRedirect() echo.MiddlewareFunc {
 | 
			
		||||
	return NonWWWRedirectWithConfig(DefaultRedirectConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
 | 
			
		||||
// See `NonWWWRedirect()`.
 | 
			
		||||
func NonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultTrailingSlashConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
	if config.Code == 0 {
 | 
			
		||||
		config.Code = DefaultRedirectConfig.Code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
			scheme := c.Scheme()
 | 
			
		||||
			host := req.Host
 | 
			
		||||
			if host[:3] == www {
 | 
			
		||||
				uri := req.RequestURI
 | 
			
		||||
				return c.Redirect(config.Code, scheme+"://"+host[4:]+uri)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return next(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										83
									
								
								vendor/github.com/labstack/echo/middleware/rewrite.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/labstack/echo/middleware/rewrite.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,83 +0,0 @@
 | 
			
		||||
package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/labstack/echo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// RewriteConfig defines the config for Rewrite middleware.
 | 
			
		||||
	RewriteConfig struct {
 | 
			
		||||
		// Skipper defines a function to skip middleware.
 | 
			
		||||
		Skipper Skipper
 | 
			
		||||
 | 
			
		||||
		// Rules defines the URL path rewrite rules. The values captured in asterisk can be
 | 
			
		||||
		// retrieved by index e.g. $1, $2 and so on.
 | 
			
		||||
		// Example:
 | 
			
		||||
		// "/old":              "/new",
 | 
			
		||||
		// "/api/*":            "/$1",
 | 
			
		||||
		// "/js/*":             "/public/javascripts/$1",
 | 
			
		||||
		// "/users/*/orders/*": "/user/$1/order/$2",
 | 
			
		||||
		// Required.
 | 
			
		||||
		Rules map[string]string `yaml:"rules"`
 | 
			
		||||
 | 
			
		||||
		rulesRegex map[*regexp.Regexp]string
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// DefaultRewriteConfig is the default Rewrite middleware config.
 | 
			
		||||
	DefaultRewriteConfig = RewriteConfig{
 | 
			
		||||
		Skipper: DefaultSkipper,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Rewrite returns a Rewrite middleware.
 | 
			
		||||
//
 | 
			
		||||
// Rewrite middleware rewrites the URL path based on the provided rules.
 | 
			
		||||
func Rewrite(rules map[string]string) echo.MiddlewareFunc {
 | 
			
		||||
	c := DefaultRewriteConfig
 | 
			
		||||
	c.Rules = rules
 | 
			
		||||
	return RewriteWithConfig(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RewriteWithConfig returns a Rewrite middleware with config.
 | 
			
		||||
// See: `Rewrite()`.
 | 
			
		||||
func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
 | 
			
		||||
	// Defaults
 | 
			
		||||
	if config.Rules == nil {
 | 
			
		||||
		panic("echo: rewrite middleware requires url path rewrite rules")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Skipper == nil {
 | 
			
		||||
		config.Skipper = DefaultBodyDumpConfig.Skipper
 | 
			
		||||
	}
 | 
			
		||||
	config.rulesRegex = map[*regexp.Regexp]string{}
 | 
			
		||||
 | 
			
		||||
	// Initialize
 | 
			
		||||
	for k, v := range config.Rules {
 | 
			
		||||
		k = strings.Replace(k, "*", "(\\S*)", -1)
 | 
			
		||||
		config.rulesRegex[regexp.MustCompile(k)] = v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := c.Request()
 | 
			
		||||
 | 
			
		||||
			// Rewrite
 | 
			
		||||
			for k, v := range config.rulesRegex {
 | 
			
		||||
				replacer := captureTokens(k, req.URL.Path)
 | 
			
		||||
				if replacer != nil {
 | 
			
		||||
					req.URL.Path = replacer.Replace(v)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return next(c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/middleware/secure.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/middleware/secure.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,12 +15,12 @@ type (
 | 
			
		||||
		// XSSProtection provides protection against cross-site scripting attack (XSS)
 | 
			
		||||
		// by setting the `X-XSS-Protection` header.
 | 
			
		||||
		// Optional. Default value "1; mode=block".
 | 
			
		||||
		XSSProtection string `yaml:"xss_protection"`
 | 
			
		||||
		XSSProtection string `json:"xss_protection"`
 | 
			
		||||
 | 
			
		||||
		// ContentTypeNosniff provides protection against overriding Content-Type
 | 
			
		||||
		// header by setting the `X-Content-Type-Options` header.
 | 
			
		||||
		// Optional. Default value "nosniff".
 | 
			
		||||
		ContentTypeNosniff string `yaml:"content_type_nosniff"`
 | 
			
		||||
		ContentTypeNosniff string `json:"content_type_nosniff"`
 | 
			
		||||
 | 
			
		||||
		// XFrameOptions can be used to indicate whether or not a browser should
 | 
			
		||||
		// be allowed to render a page in a <frame>, <iframe> or <object> .
 | 
			
		||||
@@ -32,27 +32,27 @@ type (
 | 
			
		||||
		// - "SAMEORIGIN" - The page can only be displayed in a frame on the same origin as the page itself.
 | 
			
		||||
		// - "DENY" - The page cannot be displayed in a frame, regardless of the site attempting to do so.
 | 
			
		||||
		// - "ALLOW-FROM uri" - The page can only be displayed in a frame on the specified origin.
 | 
			
		||||
		XFrameOptions string `yaml:"x_frame_options"`
 | 
			
		||||
		XFrameOptions string `json:"x_frame_options"`
 | 
			
		||||
 | 
			
		||||
		// HSTSMaxAge sets the `Strict-Transport-Security` header to indicate how
 | 
			
		||||
		// long (in seconds) browsers should remember that this site is only to
 | 
			
		||||
		// be accessed using HTTPS. This reduces your exposure to some SSL-stripping
 | 
			
		||||
		// man-in-the-middle (MITM) attacks.
 | 
			
		||||
		// Optional. Default value 0.
 | 
			
		||||
		HSTSMaxAge int `yaml:"hsts_max_age"`
 | 
			
		||||
		HSTSMaxAge int `json:"hsts_max_age"`
 | 
			
		||||
 | 
			
		||||
		// HSTSExcludeSubdomains won't include subdomains tag in the `Strict Transport Security`
 | 
			
		||||
		// header, excluding all subdomains from security policy. It has no effect
 | 
			
		||||
		// unless HSTSMaxAge is set to a non-zero value.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		HSTSExcludeSubdomains bool `yaml:"hsts_exclude_subdomains"`
 | 
			
		||||
		HSTSExcludeSubdomains bool `json:"hsts_exclude_subdomains"`
 | 
			
		||||
 | 
			
		||||
		// ContentSecurityPolicy sets the `Content-Security-Policy` header providing
 | 
			
		||||
		// security against cross-site scripting (XSS), clickjacking and other code
 | 
			
		||||
		// injection attacks resulting from execution of malicious content in the
 | 
			
		||||
		// trusted web page context.
 | 
			
		||||
		// Optional. Default value "".
 | 
			
		||||
		ContentSecurityPolicy string `yaml:"content_security_policy"`
 | 
			
		||||
		ContentSecurityPolicy string `json:"content_security_policy"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/slash.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/labstack/echo/middleware/slash.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,7 +12,7 @@ type (
 | 
			
		||||
 | 
			
		||||
		// Status code to be used when redirecting the request.
 | 
			
		||||
		// Optional, but when provided the request is redirected using this code.
 | 
			
		||||
		RedirectCode int `yaml:"redirect_code"`
 | 
			
		||||
		RedirectCode int `json:"redirect_code"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/labstack/echo/middleware/static.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -2,7 +2,6 @@ package middleware
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
@@ -19,20 +18,20 @@ type (
 | 
			
		||||
 | 
			
		||||
		// Root directory from where the static content is served.
 | 
			
		||||
		// Required.
 | 
			
		||||
		Root string `yaml:"root"`
 | 
			
		||||
		Root string `json:"root"`
 | 
			
		||||
 | 
			
		||||
		// Index file for serving a directory.
 | 
			
		||||
		// Optional. Default value "index.html".
 | 
			
		||||
		Index string `yaml:"index"`
 | 
			
		||||
		Index string `json:"index"`
 | 
			
		||||
 | 
			
		||||
		// Enable HTML5 mode by forwarding all not-found requests to root so that
 | 
			
		||||
		// SPA (single-page application) can handle the routing.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		HTML5 bool `yaml:"html5"`
 | 
			
		||||
		HTML5 bool `json:"html5"`
 | 
			
		||||
 | 
			
		||||
		// Enable directory browsing.
 | 
			
		||||
		// Optional. Default value false.
 | 
			
		||||
		Browse bool `yaml:"browse"`
 | 
			
		||||
		Browse bool `json:"browse"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -67,7 +66,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(next echo.HandlerFunc) echo.HandlerFunc {
 | 
			
		||||
		return func(c echo.Context) (err error) {
 | 
			
		||||
		return func(c echo.Context) error {
 | 
			
		||||
			if config.Skipper(c) {
 | 
			
		||||
				return next(c)
 | 
			
		||||
			}
 | 
			
		||||
@@ -76,25 +75,17 @@ 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 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
 | 
			
		||||
					if config.HTML5 && path.Ext(p) == "" {
 | 
			
		||||
						return c.File(filepath.Join(config.Root, config.Index))
 | 
			
		||||
					}
 | 
			
		||||
					return next(c)
 | 
			
		||||
				}
 | 
			
		||||
				return
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if fi.IsDir() {
 | 
			
		||||
@@ -108,7 +99,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
					if os.IsNotExist(err) {
 | 
			
		||||
						return next(c)
 | 
			
		||||
					}
 | 
			
		||||
					return
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return c.File(index)
 | 
			
		||||
@@ -119,20 +110,20 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listDir(name string, res *echo.Response) (err error) {
 | 
			
		||||
func listDir(name string, res *echo.Response) error {
 | 
			
		||||
	dir, err := os.Open(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	dirs, err := dir.Readdir(-1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create a directory index
 | 
			
		||||
	res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
 | 
			
		||||
	if _, err = fmt.Fprintf(res, "<pre>\n"); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range dirs {
 | 
			
		||||
		name := d.Name()
 | 
			
		||||
@@ -142,9 +133,9 @@ func listDir(name string, res *echo.Response) (err error) {
 | 
			
		||||
			name += "/"
 | 
			
		||||
		}
 | 
			
		||||
		if _, err = fmt.Fprintf(res, "<a href=\"%s\" style=\"color: %s;\">%s</a>\n", name, color, name); err != nil {
 | 
			
		||||
			return
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	_, err = fmt.Fprintf(res, "</pre>\n")
 | 
			
		||||
	return
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/labstack/echo/response.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -4,7 +4,6 @@ import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
@@ -12,14 +11,11 @@ type (
 | 
			
		||||
	// by an HTTP handler to construct an HTTP response.
 | 
			
		||||
	// See: https://golang.org/pkg/net/http/#ResponseWriter
 | 
			
		||||
	Response struct {
 | 
			
		||||
		echo          *Echo
 | 
			
		||||
		contentLength int64
 | 
			
		||||
		beforeFuncs   []func()
 | 
			
		||||
		afterFuncs    []func()
 | 
			
		||||
		Writer        http.ResponseWriter
 | 
			
		||||
		Status        int
 | 
			
		||||
		Size          int64
 | 
			
		||||
		Committed     bool
 | 
			
		||||
		Writer    http.ResponseWriter
 | 
			
		||||
		Status    int
 | 
			
		||||
		Size      int64
 | 
			
		||||
		Committed bool
 | 
			
		||||
		echo      *Echo
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -38,17 +34,6 @@ 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// After registers a function which is called just after the response is written.
 | 
			
		||||
// If the `Content-Length` is unknown, none of the after function is executed.
 | 
			
		||||
func (r *Response) After(fn func()) {
 | 
			
		||||
	r.afterFuncs = append(r.afterFuncs, 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
 | 
			
		||||
@@ -58,13 +43,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
 | 
			
		||||
	r.contentLength, _ = strconv.ParseInt(r.Header().Get(HeaderContentLength), 10, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write writes the data to the connection as part of an HTTP reply.
 | 
			
		||||
@@ -74,11 +55,6 @@ func (r *Response) Write(b []byte) (n int, err error) {
 | 
			
		||||
	}
 | 
			
		||||
	n, err = r.Writer.Write(b)
 | 
			
		||||
	r.Size += int64(n)
 | 
			
		||||
	if r.Size == r.contentLength {
 | 
			
		||||
		for _, fn := range r.afterFuncs {
 | 
			
		||||
			fn()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,9 +82,6 @@ func (r *Response) CloseNotify() <-chan bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Response) reset(w http.ResponseWriter) {
 | 
			
		||||
	r.contentLength = 0
 | 
			
		||||
	r.beforeFuncs = nil
 | 
			
		||||
	r.afterFuncs = nil
 | 
			
		||||
	r.Writer = w
 | 
			
		||||
	r.Size = 0
 | 
			
		||||
	r.Status = http.StatusOK
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/router.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,5 +1,7 @@
 | 
			
		||||
package echo
 | 
			
		||||
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// Router is the registry of all registered routes for an `Echo` instance for
 | 
			
		||||
	// request matching and URL path parameter parsing.
 | 
			
		||||
@@ -173,6 +175,12 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
 | 
			
		||||
				if len(cn.pnames) == 0 { // Issue #729
 | 
			
		||||
					cn.pnames = pnames
 | 
			
		||||
				}
 | 
			
		||||
				for i, n := range pnames {
 | 
			
		||||
					// Param name aliases
 | 
			
		||||
					if i < len(cn.pnames) && !strings.Contains(cn.pnames[i], n) {
 | 
			
		||||
						cn.pnames[i] += "," + n
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
@@ -386,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 = cn.parent // Next (Issue #954)
 | 
			
		||||
				nn = nil // Next
 | 
			
		||||
				search = ns
 | 
			
		||||
				if nk == pkind {
 | 
			
		||||
					goto Param
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/labstack/echo/util_go17.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,12 +0,0 @@
 | 
			
		||||
// +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
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/labstack/echo/util_go18.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,10 +0,0 @@
 | 
			
		||||
// +build go1.8
 | 
			
		||||
 | 
			
		||||
package echo
 | 
			
		||||
 | 
			
		||||
import "net/url"
 | 
			
		||||
 | 
			
		||||
// PathUnescape is wraps `url.PathUnescape`
 | 
			
		||||
func PathUnescape(s string) (string, error) {
 | 
			
		||||
	return url.PathUnescape(s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										201
									
								
								vendor/github.com/matterbridge/gomatrix/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/matterbridge/gomatrix/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,201 +0,0 @@
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "{}"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright {yyyy} {name of copyright owner}
 | 
			
		||||
 | 
			
		||||
   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.
 | 
			
		||||
							
								
								
									
										703
									
								
								vendor/github.com/matterbridge/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										703
									
								
								vendor/github.com/matterbridge/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,703 +0,0 @@
 | 
			
		||||
// Package gomatrix implements the Matrix Client-Server API.
 | 
			
		||||
//
 | 
			
		||||
// Specification can be found at http://matrix.org/docs/spec/client_server/r0.2.0.html
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client represents a Matrix client.
 | 
			
		||||
type Client struct {
 | 
			
		||||
	HomeserverURL *url.URL     // The base homeserver URL
 | 
			
		||||
	Prefix        string       // The API prefix eg '/_matrix/client/r0'
 | 
			
		||||
	UserID        string       // The user ID of the client. Used for forming HTTP paths which use the client's user ID.
 | 
			
		||||
	AccessToken   string       // The access_token for the client.
 | 
			
		||||
	Client        *http.Client // The underlying HTTP client which will be used to make HTTP requests.
 | 
			
		||||
	Syncer        Syncer       // The thing which can process /sync responses
 | 
			
		||||
	Store         Storer       // The thing which can store rooms/tokens/ids
 | 
			
		||||
 | 
			
		||||
	// The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty,
 | 
			
		||||
	// no user_id parameter will be sent.
 | 
			
		||||
	// See http://matrix.org/docs/spec/application_service/unstable.html#identity-assertion
 | 
			
		||||
	AppServiceUserID string
 | 
			
		||||
 | 
			
		||||
	syncingMutex sync.Mutex // protects syncingID
 | 
			
		||||
	syncingID    uint32     // Identifies the current Sync. Only one Sync can be active at any given time.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
 | 
			
		||||
type HTTPError struct {
 | 
			
		||||
	WrappedError error
 | 
			
		||||
	Message      string
 | 
			
		||||
	Code         int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e HTTPError) Error() string {
 | 
			
		||||
	var wrappedErrMsg string
 | 
			
		||||
	if e.WrappedError != nil {
 | 
			
		||||
		wrappedErrMsg = e.WrappedError.Error()
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("msg=%s code=%d wrapped=%s", e.Message, e.Code, wrappedErrMsg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildURL builds a URL with the Client's homserver/prefix/access_token set already.
 | 
			
		||||
func (cli *Client) BuildURL(urlPath ...string) string {
 | 
			
		||||
	ps := []string{cli.Prefix}
 | 
			
		||||
	for _, p := range urlPath {
 | 
			
		||||
		ps = append(ps, p)
 | 
			
		||||
	}
 | 
			
		||||
	return cli.BuildBaseURL(ps...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildBaseURL builds a URL with the Client's homeserver/access_token set already. You must
 | 
			
		||||
// supply the prefix in the path.
 | 
			
		||||
func (cli *Client) BuildBaseURL(urlPath ...string) string {
 | 
			
		||||
	// copy the URL. Purposefully ignore error as the input is from a valid URL already
 | 
			
		||||
	hsURL, _ := url.Parse(cli.HomeserverURL.String())
 | 
			
		||||
	parts := []string{hsURL.Path}
 | 
			
		||||
	parts = append(parts, urlPath...)
 | 
			
		||||
	hsURL.Path = path.Join(parts...)
 | 
			
		||||
	query := hsURL.Query()
 | 
			
		||||
	if cli.AccessToken != "" {
 | 
			
		||||
		query.Set("access_token", cli.AccessToken)
 | 
			
		||||
	}
 | 
			
		||||
	if cli.AppServiceUserID != "" {
 | 
			
		||||
		query.Set("user_id", cli.AppServiceUserID)
 | 
			
		||||
	}
 | 
			
		||||
	hsURL.RawQuery = query.Encode()
 | 
			
		||||
	return hsURL.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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()
 | 
			
		||||
	for k, v := range urlQuery {
 | 
			
		||||
		q.Set(k, v)
 | 
			
		||||
	}
 | 
			
		||||
	u.RawQuery = q.Encode()
 | 
			
		||||
	return u.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCredentials sets the user ID and access token on this client instance.
 | 
			
		||||
func (cli *Client) SetCredentials(userID, accessToken string) {
 | 
			
		||||
	cli.AccessToken = accessToken
 | 
			
		||||
	cli.UserID = userID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearCredentials removes the user ID and access token on this client instance.
 | 
			
		||||
func (cli *Client) ClearCredentials() {
 | 
			
		||||
	cli.AccessToken = ""
 | 
			
		||||
	cli.UserID = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the
 | 
			
		||||
// error will be nil.
 | 
			
		||||
//
 | 
			
		||||
// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine.
 | 
			
		||||
// Fatal sync errors can be caused by:
 | 
			
		||||
//   - The failure to create a filter.
 | 
			
		||||
//   - Client.Syncer.OnFailedSync returning an error in response to a failed sync.
 | 
			
		||||
//   - Client.Syncer.ProcessResponse returning an error.
 | 
			
		||||
// If you wish to continue retrying in spite of these fatal errors, call Sync() again.
 | 
			
		||||
func (cli *Client) Sync() error {
 | 
			
		||||
	// Mark the client as syncing.
 | 
			
		||||
	// We will keep syncing until the syncing state changes. Either because
 | 
			
		||||
	// Sync is called or StopSync is called.
 | 
			
		||||
	syncingID := cli.incrementSyncingID()
 | 
			
		||||
	nextBatch := cli.Store.LoadNextBatch(cli.UserID)
 | 
			
		||||
	filterID := cli.Store.LoadFilterID(cli.UserID)
 | 
			
		||||
	if filterID == "" {
 | 
			
		||||
		filterJSON := cli.Syncer.GetFilterJSON(cli.UserID)
 | 
			
		||||
		resFilter, err := cli.CreateFilter(filterJSON)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		filterID = resFilter.FilterID
 | 
			
		||||
		cli.Store.SaveFilterID(cli.UserID, filterID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		resSync, err := cli.SyncRequest(30000, nextBatch, filterID, false, "")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			duration, err2 := cli.Syncer.OnFailedSync(resSync, err)
 | 
			
		||||
			if err2 != nil {
 | 
			
		||||
				return err2
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(duration)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check that the syncing state hasn't changed
 | 
			
		||||
		// Either because we've stopped syncing or another sync has been started.
 | 
			
		||||
		// We discard the response from our sync.
 | 
			
		||||
		if cli.getSyncingID() != syncingID {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Save the token now *before* processing it. This means it's possible
 | 
			
		||||
		// to not process some events, but it means that we won't get constantly stuck processing
 | 
			
		||||
		// a malformed/buggy event which keeps making us panic.
 | 
			
		||||
		cli.Store.SaveNextBatch(cli.UserID, resSync.NextBatch)
 | 
			
		||||
		if err = cli.Syncer.ProcessResponse(resSync, nextBatch); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nextBatch = resSync.NextBatch
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *Client) incrementSyncingID() uint32 {
 | 
			
		||||
	cli.syncingMutex.Lock()
 | 
			
		||||
	defer cli.syncingMutex.Unlock()
 | 
			
		||||
	cli.syncingID++
 | 
			
		||||
	return cli.syncingID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *Client) getSyncingID() uint32 {
 | 
			
		||||
	cli.syncingMutex.Lock()
 | 
			
		||||
	defer cli.syncingMutex.Unlock()
 | 
			
		||||
	return cli.syncingID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StopSync stops the ongoing sync started by Sync.
 | 
			
		||||
func (cli *Client) StopSync() {
 | 
			
		||||
	// Advance the syncing state so that any running Syncs will terminate.
 | 
			
		||||
	cli.incrementSyncingID()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRequest makes a JSON HTTP request to the given URL.
 | 
			
		||||
// If "resBody" is not nil, the response body will be json.Unmarshalled into it.
 | 
			
		||||
//
 | 
			
		||||
// Returns the HTTP body as bytes on 2xx with a nil error. Returns an error if the response is not 2xx along
 | 
			
		||||
// with the HTTP body bytes if it got that far. This error is an HTTPError which includes the returned
 | 
			
		||||
// HTTP status code and possibly a RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
 | 
			
		||||
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
 | 
			
		||||
	var req *http.Request
 | 
			
		||||
	var err error
 | 
			
		||||
	if reqBody != nil {
 | 
			
		||||
		var jsonStr []byte
 | 
			
		||||
		jsonStr, err = json.Marshal(reqBody)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr))
 | 
			
		||||
	} else {
 | 
			
		||||
		req, err = http.NewRequest(method, httpURL, nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Content-Type", "application/json")
 | 
			
		||||
	res, err := cli.Client.Do(req)
 | 
			
		||||
	if res != nil {
 | 
			
		||||
		defer res.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	contents, err := ioutil.ReadAll(res.Body)
 | 
			
		||||
	if res.StatusCode/100 != 2 { // not 2xx
 | 
			
		||||
		var wrap error
 | 
			
		||||
		var respErr RespError
 | 
			
		||||
		if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" {
 | 
			
		||||
			wrap = respErr
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we failed to decode as RespError, don't just drop the HTTP body, include it in the
 | 
			
		||||
		// HTTP error instead (e.g proxy errors which return HTML).
 | 
			
		||||
		msg := "Failed to " + method + " JSON to " + req.URL.Path
 | 
			
		||||
		if wrap == nil {
 | 
			
		||||
			msg = msg + ": " + string(contents)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return contents, HTTPError{
 | 
			
		||||
			Code:         res.StatusCode,
 | 
			
		||||
			Message:      msg,
 | 
			
		||||
			WrappedError: wrap,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resBody != nil {
 | 
			
		||||
		if err = json.Unmarshal(contents, &resBody); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return contents, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
 | 
			
		||||
func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("user", cli.UserID, "filter")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, &filter, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SyncRequest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
 | 
			
		||||
func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bool, setPresence string) (resp *RespSync, err error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"timeout": strconv.Itoa(timeout),
 | 
			
		||||
	}
 | 
			
		||||
	if since != "" {
 | 
			
		||||
		query["since"] = since
 | 
			
		||||
	}
 | 
			
		||||
	if filterID != "" {
 | 
			
		||||
		query["filter"] = filterID
 | 
			
		||||
	}
 | 
			
		||||
	if setPresence != "" {
 | 
			
		||||
		query["set_presence"] = setPresence
 | 
			
		||||
	}
 | 
			
		||||
	if fullState {
 | 
			
		||||
		query["full_state"] = "true"
 | 
			
		||||
	}
 | 
			
		||||
	urlPath := cli.BuildURLWithQuery([]string{"sync"}, query)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
 | 
			
		||||
	var bodyBytes []byte
 | 
			
		||||
	bodyBytes, err = cli.MakeRequest("POST", u, req, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		httpErr, ok := err.(HTTPError)
 | 
			
		||||
		if !ok { // network error
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if httpErr.Code == 401 {
 | 
			
		||||
			// body should be RespUserInteractive, if it isn't, fail with the error
 | 
			
		||||
			err = json.Unmarshal(bodyBytes, &uiaResp)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// body should be RespRegister
 | 
			
		||||
	err = json.Unmarshal(bodyBytes, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
//
 | 
			
		||||
// Registers with kind=user. For kind=guest, see RegisterGuest.
 | 
			
		||||
func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
 | 
			
		||||
	u := cli.BuildURL("register")
 | 
			
		||||
	return cli.register(u, req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
// with kind=guest.
 | 
			
		||||
//
 | 
			
		||||
// For kind=user, see Register.
 | 
			
		||||
func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"kind": "guest",
 | 
			
		||||
	}
 | 
			
		||||
	u := cli.BuildURLWithQuery([]string{"register"}, query)
 | 
			
		||||
	return cli.register(u, req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth
 | 
			
		||||
//
 | 
			
		||||
// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration
 | 
			
		||||
// this way. If the homeserver does not, an error is returned.
 | 
			
		||||
//
 | 
			
		||||
// This does not set credentials on the client instance. See SetCredentials() instead.
 | 
			
		||||
//
 | 
			
		||||
// 	res, err := cli.RegisterDummy(&gomatrix.ReqRegister{
 | 
			
		||||
//		Username: "alice",
 | 
			
		||||
//		Password: "wonderland",
 | 
			
		||||
//	})
 | 
			
		||||
//  if err != nil {
 | 
			
		||||
// 		panic(err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	token := res.AccessToken
 | 
			
		||||
func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
 | 
			
		||||
	res, uia, err := cli.Register(req)
 | 
			
		||||
	if err != nil && uia == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if uia != nil && uia.HasSingleStageFlow("m.login.dummy") {
 | 
			
		||||
		req.Auth = struct {
 | 
			
		||||
			Type    string `json:"type"`
 | 
			
		||||
			Session string `json:"session,omitempty"`
 | 
			
		||||
		}{"m.login.dummy", uia.Session}
 | 
			
		||||
		res, _, err = cli.Register(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if res == nil {
 | 
			
		||||
		return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?")
 | 
			
		||||
	}
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Login a user to the homeserver according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
 | 
			
		||||
// This does not set credentials on this client instance. See SetCredentials() instead.
 | 
			
		||||
func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("login")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
 | 
			
		||||
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
 | 
			
		||||
func (cli *Client) Logout() (resp *RespLogout, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("logout")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
 | 
			
		||||
func (cli *Client) Versions() (resp *RespVersions, err error) {
 | 
			
		||||
	urlPath := cli.BuildBaseURL("_matrix", "client", "versions")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias
 | 
			
		||||
//
 | 
			
		||||
// If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will
 | 
			
		||||
// be JSON encoded and used as the request body.
 | 
			
		||||
func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{}) (resp *RespJoinRoom, err error) {
 | 
			
		||||
	var urlPath string
 | 
			
		||||
	if serverName != "" {
 | 
			
		||||
		urlPath = cli.BuildURLWithQuery([]string{"join", roomIDorAlias}, map[string]string{
 | 
			
		||||
			"server_name": serverName,
 | 
			
		||||
		})
 | 
			
		||||
	} else {
 | 
			
		||||
		urlPath = cli.BuildURL("join", roomIDorAlias)
 | 
			
		||||
	}
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, content, &resp)
 | 
			
		||||
	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")
 | 
			
		||||
	s := struct {
 | 
			
		||||
		DisplayName string `json:"displayname"`
 | 
			
		||||
	}{displayName}
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url
 | 
			
		||||
func (cli *Client) GetAvatarURL() (url string, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
 | 
			
		||||
	s := struct {
 | 
			
		||||
		AvatarURL string `json:"avatar_url"`
 | 
			
		||||
	}{}
 | 
			
		||||
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.AvatarURL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url
 | 
			
		||||
func (cli *Client) SetAvatarURL(url string) (err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
 | 
			
		||||
	s := struct {
 | 
			
		||||
		AvatarURL string `json:"avatar_url"`
 | 
			
		||||
	}{url}
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
 | 
			
		||||
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
 | 
			
		||||
func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) {
 | 
			
		||||
	txnID := txnID()
 | 
			
		||||
	urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
 | 
			
		||||
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
 | 
			
		||||
func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendText sends an m.room.message event into the given room with a msgtype of m.text
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
 | 
			
		||||
func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		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()
 | 
			
		||||
	urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
//  resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{
 | 
			
		||||
//  	Preset: "public_chat",
 | 
			
		||||
//  })
 | 
			
		||||
//  fmt.Println("Room:", resp.RoomID)
 | 
			
		||||
func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("createRoom")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
 | 
			
		||||
func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "leave")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
 | 
			
		||||
func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "forget")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
 | 
			
		||||
func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "invite")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint
 | 
			
		||||
func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "invite")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
 | 
			
		||||
func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "kick")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
 | 
			
		||||
func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "ban")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "unban")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	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
 | 
			
		||||
func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", u, nil, outContent)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadLink uploads an HTTP URL and then returns an MXC URI.
 | 
			
		||||
func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
 | 
			
		||||
	res, err := cli.Client.Get(link)
 | 
			
		||||
	if res != nil {
 | 
			
		||||
		defer res.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
 | 
			
		||||
func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) {
 | 
			
		||||
	req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Content-Type", contentType)
 | 
			
		||||
	req.ContentLength = contentLength
 | 
			
		||||
	res, err := cli.Client.Do(req)
 | 
			
		||||
	if res != nil {
 | 
			
		||||
		defer res.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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: " + string(contents),
 | 
			
		||||
			Code:    res.StatusCode,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var m RespMediaUpload
 | 
			
		||||
	if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &m, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinedMembers returns a map of joined room members. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
//
 | 
			
		||||
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
 | 
			
		||||
// This API is primarily designed for application services which may want to efficiently look up joined members in a room.
 | 
			
		||||
func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "joined_members")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", u, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinedRooms returns a list of rooms which the client is joined to. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
//
 | 
			
		||||
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
 | 
			
		||||
// This API is primarily designed for application services which may want to efficiently look up joined rooms.
 | 
			
		||||
func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
 | 
			
		||||
	u := cli.BuildURL("joined_rooms")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", u, nil, &resp)
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient creates a new Matrix Client ready for syncing
 | 
			
		||||
func NewClient(homeserverURL, userID, accessToken string) (*Client, error) {
 | 
			
		||||
	hsURL, err := url.Parse(homeserverURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// By default, use an in-memory store which will never save filter ids / next batch tokens to disk.
 | 
			
		||||
	// The client will work with this storer: it just won't remember across restarts.
 | 
			
		||||
	// In practice, a database backend should be used.
 | 
			
		||||
	store := NewInMemoryStore()
 | 
			
		||||
	cli := Client{
 | 
			
		||||
		AccessToken:   accessToken,
 | 
			
		||||
		HomeserverURL: hsURL,
 | 
			
		||||
		UserID:        userID,
 | 
			
		||||
		Prefix:        "/_matrix/client/r0",
 | 
			
		||||
		Syncer:        NewDefaultSyncer(userID, store),
 | 
			
		||||
		Store:         store,
 | 
			
		||||
	}
 | 
			
		||||
	// By default, use the default HTTP client.
 | 
			
		||||
	cli.Client = http.DefaultClient
 | 
			
		||||
 | 
			
		||||
	return &cli, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										102
									
								
								vendor/github.com/matterbridge/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/matterbridge/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,102 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"html"
 | 
			
		||||
	"regexp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Event represents a single Matrix event.
 | 
			
		||||
type Event struct {
 | 
			
		||||
	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.
 | 
			
		||||
	Redacts   string                 `json:"redacts,omitempty"`   // The event ID that was redacted if a m.room.redaction event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Body returns the value of the "body" key in the event content if it is
 | 
			
		||||
// present and is a string.
 | 
			
		||||
func (event *Event) Body() (body string, ok bool) {
 | 
			
		||||
	value, exists := event.Content["body"]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	body, ok = value.(string)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MessageType returns the value of the "msgtype" key in the event content if
 | 
			
		||||
// it is present and is a string.
 | 
			
		||||
func (event *Event) MessageType() (msgtype string, ok bool) {
 | 
			
		||||
	value, exists := event.Content["msgtype"]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msgtype, ok = value.(string)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TextMessage is the contents of a Matrix formated message event.
 | 
			
		||||
type TextMessage struct {
 | 
			
		||||
	MsgType string `json:"msgtype"`
 | 
			
		||||
	Body    string `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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,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
 | 
			
		||||
type ImageMessage struct {
 | 
			
		||||
	MsgType string    `json:"msgtype"`
 | 
			
		||||
	Body    string    `json:"body"`
 | 
			
		||||
	URL     string    `json:"url"`
 | 
			
		||||
	Info    ImageInfo `json:"info"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An HTMLMessage is the contents of a Matrix HTML formated message event.
 | 
			
		||||
type HTMLMessage struct {
 | 
			
		||||
	Body          string `json:"body"`
 | 
			
		||||
	MsgType       string `json:"msgtype"`
 | 
			
		||||
	Format        string `json:"format"`
 | 
			
		||||
	FormattedBody string `json:"formatted_body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var htmlRegex = regexp.MustCompile("<[^<]+?>")
 | 
			
		||||
 | 
			
		||||
// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition
 | 
			
		||||
// to the provided HTML.
 | 
			
		||||
func GetHTMLMessage(msgtype, htmlText string) HTMLMessage {
 | 
			
		||||
	return HTMLMessage{
 | 
			
		||||
		Body:          html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
 | 
			
		||||
		MsgType:       msgtype,
 | 
			
		||||
		Format:        "org.matrix.custom.html",
 | 
			
		||||
		FormattedBody: htmlText,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/matterbridge/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matterbridge/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,43 +0,0 @@
 | 
			
		||||
// 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"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								vendor/github.com/matterbridge/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/matterbridge/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,78 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
type ReqRegister struct {
 | 
			
		||||
	Username                 string      `json:"username,omitempty"`
 | 
			
		||||
	BindEmail                bool        `json:"bind_email,omitempty"`
 | 
			
		||||
	Password                 string      `json:"password,omitempty"`
 | 
			
		||||
	DeviceID                 string      `json:"device_id,omitempty"`
 | 
			
		||||
	InitialDeviceDisplayName string      `json:"initial_device_display_name"`
 | 
			
		||||
	Auth                     interface{} `json:"auth,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
 | 
			
		||||
type ReqLogin struct {
 | 
			
		||||
	Type                     string `json:"type"`
 | 
			
		||||
	Password                 string `json:"password,omitempty"`
 | 
			
		||||
	Medium                   string `json:"medium,omitempty"`
 | 
			
		||||
	User                     string `json:"user,omitempty"`
 | 
			
		||||
	Address                  string `json:"address,omitempty"`
 | 
			
		||||
	Token                    string `json:"token,omitempty"`
 | 
			
		||||
	DeviceID                 string `json:"device_id,omitempty"`
 | 
			
		||||
	InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
type ReqCreateRoom struct {
 | 
			
		||||
	Visibility      string                 `json:"visibility,omitempty"`
 | 
			
		||||
	RoomAliasName   string                 `json:"room_alias_name,omitempty"`
 | 
			
		||||
	Name            string                 `json:"name,omitempty"`
 | 
			
		||||
	Topic           string                 `json:"topic,omitempty"`
 | 
			
		||||
	Invite          []string               `json:"invite,omitempty"`
 | 
			
		||||
	Invite3PID      []ReqInvite3PID        `json:"invite_3pid,omitempty"`
 | 
			
		||||
	CreationContent map[string]interface{} `json:"creation_content,omitempty"`
 | 
			
		||||
	InitialState    []Event                `json:"initial_state,omitempty"`
 | 
			
		||||
	Preset          string                 `json:"preset,omitempty"`
 | 
			
		||||
	IsDirect        bool                   `json:"is_direct,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
 | 
			
		||||
type ReqRedact struct {
 | 
			
		||||
	Reason string `json:"reason,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57
 | 
			
		||||
// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
type ReqInvite3PID struct {
 | 
			
		||||
	IDServer string `json:"id_server"`
 | 
			
		||||
	Medium   string `json:"medium"`
 | 
			
		||||
	Address  string `json:"address"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
 | 
			
		||||
type ReqInviteUser struct {
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
 | 
			
		||||
type ReqKickUser struct {
 | 
			
		||||
	Reason string `json:"reason,omitempty"`
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
 | 
			
		||||
type ReqBanUser struct {
 | 
			
		||||
	Reason string `json:"reason,omitempty"`
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										176
									
								
								vendor/github.com/matterbridge/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										176
									
								
								vendor/github.com/matterbridge/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,176 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
 | 
			
		||||
type RespError struct {
 | 
			
		||||
	ErrCode string `json:"errcode"`
 | 
			
		||||
	Err     string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns the errcode and error message.
 | 
			
		||||
func (e RespError) Error() string {
 | 
			
		||||
	return e.ErrCode + ": " + e.Err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
 | 
			
		||||
type RespCreateFilter struct {
 | 
			
		||||
	FilterID string `json:"filter_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
 | 
			
		||||
type RespVersions struct {
 | 
			
		||||
	Versions []string `json:"versions"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join
 | 
			
		||||
type RespJoinRoom struct {
 | 
			
		||||
	RoomID string `json:"room_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
 | 
			
		||||
type RespLeaveRoom struct{}
 | 
			
		||||
 | 
			
		||||
// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
 | 
			
		||||
type RespForgetRoom struct{}
 | 
			
		||||
 | 
			
		||||
// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
 | 
			
		||||
type RespInviteUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
 | 
			
		||||
type RespKickUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespJoinedMembers is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
type RespJoinedMembers struct {
 | 
			
		||||
	Joined map[string]struct {
 | 
			
		||||
		DisplayName *string `json:"display_name"`
 | 
			
		||||
		AvatarURL   *string `json:"avatar_url"`
 | 
			
		||||
	} `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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
 | 
			
		||||
type RespMediaUpload struct {
 | 
			
		||||
	ContentURI string `json:"content_uri"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
 | 
			
		||||
type RespUserInteractive struct {
 | 
			
		||||
	Flows []struct {
 | 
			
		||||
		Stages []string `json:"stages"`
 | 
			
		||||
	} `json:"flows"`
 | 
			
		||||
	Params    map[string]interface{} `json:"params"`
 | 
			
		||||
	Session   string                 `json:"string"`
 | 
			
		||||
	Completed []string               `json:"completed"`
 | 
			
		||||
	ErrCode   string                 `json:"errcode"`
 | 
			
		||||
	Error     string                 `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
 | 
			
		||||
func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
 | 
			
		||||
	for _, f := range r.Flows {
 | 
			
		||||
		if len(f.Stages) == 1 && f.Stages[0] == stageName {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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"`
 | 
			
		||||
	DeviceID     string `json:"device_id"`
 | 
			
		||||
	HomeServer   string `json:"home_server"`
 | 
			
		||||
	RefreshToken string `json:"refresh_token"`
 | 
			
		||||
	UserID       string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
 | 
			
		||||
type RespLogin struct {
 | 
			
		||||
	AccessToken string `json:"access_token"`
 | 
			
		||||
	DeviceID    string `json:"device_id"`
 | 
			
		||||
	HomeServer  string `json:"home_server"`
 | 
			
		||||
	UserID      string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
 | 
			
		||||
type RespLogout struct{}
 | 
			
		||||
 | 
			
		||||
// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
type RespCreateRoom struct {
 | 
			
		||||
	RoomID string `json:"room_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
 | 
			
		||||
type RespSync struct {
 | 
			
		||||
	NextBatch   string `json:"next_batch"`
 | 
			
		||||
	AccountData struct {
 | 
			
		||||
		Events []Event `json:"events"`
 | 
			
		||||
	} `json:"account_data"`
 | 
			
		||||
	Presence 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"`
 | 
			
		||||
			} `json:"state"`
 | 
			
		||||
			Timeline struct {
 | 
			
		||||
				Events    []Event `json:"events"`
 | 
			
		||||
				Limited   bool    `json:"limited"`
 | 
			
		||||
				PrevBatch string  `json:"prev_batch"`
 | 
			
		||||
			} `json:"timeline"`
 | 
			
		||||
		} `json:"join"`
 | 
			
		||||
		Invite map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event
 | 
			
		||||
			} `json:"invite_state"`
 | 
			
		||||
		} `json:"invite"`
 | 
			
		||||
	} `json:"rooms"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RespTurnServer struct {
 | 
			
		||||
	Username string   `json:"username"`
 | 
			
		||||
	Password string   `json:"password"`
 | 
			
		||||
	TTL      int      `json:"ttl"`
 | 
			
		||||
	URIs     []string `json:"uris"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								vendor/github.com/matterbridge/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/matterbridge/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,50 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// Room represents a single Matrix room.
 | 
			
		||||
type Room struct {
 | 
			
		||||
	ID    string
 | 
			
		||||
	State map[string]map[string]*Event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateState updates the room's current state with the given Event. This will clobber events based
 | 
			
		||||
// on the type/state_key combination.
 | 
			
		||||
func (room Room) UpdateState(event *Event) {
 | 
			
		||||
	_, exists := room.State[event.Type]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		room.State[event.Type] = make(map[string]*Event)
 | 
			
		||||
	}
 | 
			
		||||
	room.State[event.Type][*event.StateKey] = event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
 | 
			
		||||
func (room Room) GetStateEvent(eventType string, stateKey string) *Event {
 | 
			
		||||
	stateEventMap, _ := room.State[eventType]
 | 
			
		||||
	event, _ := stateEventMap[stateKey]
 | 
			
		||||
	return event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMembershipState returns the membership state of the given user ID in this room. If there is
 | 
			
		||||
// no entry for this member, 'leave' is returned for consistency with left users.
 | 
			
		||||
func (room Room) GetMembershipState(userID string) string {
 | 
			
		||||
	state := "leave"
 | 
			
		||||
	event := room.GetStateEvent("m.room.member", userID)
 | 
			
		||||
	if event != nil {
 | 
			
		||||
		membershipState, found := event.Content["membership"]
 | 
			
		||||
		if found {
 | 
			
		||||
			mState, isString := membershipState.(string)
 | 
			
		||||
			if isString {
 | 
			
		||||
				state = mState
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRoom creates a new Room with the given ID
 | 
			
		||||
func NewRoom(roomID string) *Room {
 | 
			
		||||
	// Init the State map and return a pointer to the Room
 | 
			
		||||
	return &Room{
 | 
			
		||||
		ID:    roomID,
 | 
			
		||||
		State: make(map[string]map[string]*Event),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/matterbridge/gomatrix/store.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/matterbridge/gomatrix/store.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,65 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// Storer is an interface which must be satisfied to store client data.
 | 
			
		||||
//
 | 
			
		||||
// You can either write a struct which persists this data to disk, or you can use the
 | 
			
		||||
// provided "InMemoryStore" which just keeps data around in-memory which is lost on
 | 
			
		||||
// restarts.
 | 
			
		||||
type Storer interface {
 | 
			
		||||
	SaveFilterID(userID, filterID string)
 | 
			
		||||
	LoadFilterID(userID string) string
 | 
			
		||||
	SaveNextBatch(userID, nextBatchToken string)
 | 
			
		||||
	LoadNextBatch(userID string) string
 | 
			
		||||
	SaveRoom(room *Room)
 | 
			
		||||
	LoadRoom(roomID string) *Room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InMemoryStore implements the Storer interface.
 | 
			
		||||
//
 | 
			
		||||
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
 | 
			
		||||
// or next batch tokens on any goroutine other than the syncing goroutine: the one
 | 
			
		||||
// which called Client.Sync().
 | 
			
		||||
type InMemoryStore struct {
 | 
			
		||||
	Filters   map[string]string
 | 
			
		||||
	NextBatch map[string]string
 | 
			
		||||
	Rooms     map[string]*Room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveFilterID to memory.
 | 
			
		||||
func (s *InMemoryStore) SaveFilterID(userID, filterID string) {
 | 
			
		||||
	s.Filters[userID] = filterID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadFilterID from memory.
 | 
			
		||||
func (s *InMemoryStore) LoadFilterID(userID string) string {
 | 
			
		||||
	return s.Filters[userID]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveNextBatch to memory.
 | 
			
		||||
func (s *InMemoryStore) SaveNextBatch(userID, nextBatchToken string) {
 | 
			
		||||
	s.NextBatch[userID] = nextBatchToken
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadNextBatch from memory.
 | 
			
		||||
func (s *InMemoryStore) LoadNextBatch(userID string) string {
 | 
			
		||||
	return s.NextBatch[userID]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveRoom to memory.
 | 
			
		||||
func (s *InMemoryStore) SaveRoom(room *Room) {
 | 
			
		||||
	s.Rooms[room.ID] = room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadRoom from memory.
 | 
			
		||||
func (s *InMemoryStore) LoadRoom(roomID string) *Room {
 | 
			
		||||
	return s.Rooms[roomID]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInMemoryStore constructs a new InMemoryStore.
 | 
			
		||||
func NewInMemoryStore() *InMemoryStore {
 | 
			
		||||
	return &InMemoryStore{
 | 
			
		||||
		Filters:   make(map[string]string),
 | 
			
		||||
		NextBatch: make(map[string]string),
 | 
			
		||||
		Rooms:     make(map[string]*Room),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								vendor/github.com/matterbridge/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										164
									
								
								vendor/github.com/matterbridge/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,164 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Syncer represents an interface that must be satisfied in order to do /sync requests on a client.
 | 
			
		||||
type Syncer interface {
 | 
			
		||||
	// Process the /sync response. The since parameter is the since= value that was used to produce the response.
 | 
			
		||||
	// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped
 | 
			
		||||
	// permanently.
 | 
			
		||||
	ProcessResponse(resp *RespSync, since string) error
 | 
			
		||||
	// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
 | 
			
		||||
	OnFailedSync(res *RespSync, err error) (time.Duration, error)
 | 
			
		||||
	// GetFilterJSON for the given user ID. NOT the filter ID.
 | 
			
		||||
	GetFilterJSON(userID string) json.RawMessage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
 | 
			
		||||
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
 | 
			
		||||
// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
 | 
			
		||||
type DefaultSyncer struct {
 | 
			
		||||
	UserID    string
 | 
			
		||||
	Store     Storer
 | 
			
		||||
	listeners map[string][]OnEventListener // event type to listeners array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
 | 
			
		||||
type OnEventListener func(*Event)
 | 
			
		||||
 | 
			
		||||
// NewDefaultSyncer returns an instantiated DefaultSyncer
 | 
			
		||||
func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer {
 | 
			
		||||
	return &DefaultSyncer{
 | 
			
		||||
		UserID:    userID,
 | 
			
		||||
		Store:     store,
 | 
			
		||||
		listeners: make(map[string][]OnEventListener),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
 | 
			
		||||
// unrepeating events. Returns a fatal error if a listener panics.
 | 
			
		||||
func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
 | 
			
		||||
	if !s.shouldProcessResponse(res, since) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack())
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Join {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.State.Events {
 | 
			
		||||
			event.RoomID = roomID
 | 
			
		||||
			room.UpdateState(&event)
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
		for _, event := range roomData.Timeline.Events {
 | 
			
		||||
			event.RoomID = roomID
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Invite {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.State.Events {
 | 
			
		||||
			event.RoomID = roomID
 | 
			
		||||
			room.UpdateState(&event)
 | 
			
		||||
			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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnEventType allows callers to be notified when there are new events for the given event type.
 | 
			
		||||
// There are no duplicate checks.
 | 
			
		||||
func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) {
 | 
			
		||||
	_, exists := s.listeners[eventType]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		s.listeners[eventType] = []OnEventListener{}
 | 
			
		||||
	}
 | 
			
		||||
	s.listeners[eventType] = append(s.listeners[eventType], callback)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
 | 
			
		||||
// stuff that shouldn't be processed.
 | 
			
		||||
func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool {
 | 
			
		||||
	if since == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	// This is a horrible hack because /sync will return the most recent messages for a room
 | 
			
		||||
	// as soon as you /join it. We do NOT want to process those events in that particular room
 | 
			
		||||
	// because they may have already been processed (if you toggle the bot in/out of the room).
 | 
			
		||||
	//
 | 
			
		||||
	// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
 | 
			
		||||
	// exists and is "join" and then discard processing that room entirely if so.
 | 
			
		||||
	// TODO: We probably want to process messages from after the last join event in the timeline.
 | 
			
		||||
	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 != nil && *e.StateKey == s.UserID {
 | 
			
		||||
				m := e.Content["membership"]
 | 
			
		||||
				mship, ok := m.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if mship == "join" {
 | 
			
		||||
					_, ok := resp.Rooms.Join[roomID]
 | 
			
		||||
					if !ok {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					delete(resp.Rooms.Join, roomID)   // don't re-process messages
 | 
			
		||||
					delete(resp.Rooms.Invite, roomID) // don't re-process invites
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse()
 | 
			
		||||
func (s *DefaultSyncer) getOrCreateRoom(roomID string) *Room {
 | 
			
		||||
	room := s.Store.LoadRoom(roomID)
 | 
			
		||||
	if room == nil { // create a new Room
 | 
			
		||||
		room = NewRoom(roomID)
 | 
			
		||||
		s.Store.SaveRoom(room)
 | 
			
		||||
	}
 | 
			
		||||
	return room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DefaultSyncer) notifyListeners(event *Event) {
 | 
			
		||||
	listeners, exists := s.listeners[event.Type]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, fn := range listeners {
 | 
			
		||||
		fn(event)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
 | 
			
		||||
func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
 | 
			
		||||
	return 10 * time.Second, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFilterJSON returns a filter with a timeline limit of 50.
 | 
			
		||||
func (s *DefaultSyncer) GetFilterJSON(userID string) json.RawMessage {
 | 
			
		||||
	return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								vendor/github.com/matterbridge/gomatrix/userids.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										130
									
								
								vendor/github.com/matterbridge/gomatrix/userids.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,130 +0,0 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const lowerhex = "0123456789abcdef"
 | 
			
		||||
 | 
			
		||||
// encode the given byte using quoted-printable encoding (e.g "=2f")
 | 
			
		||||
// and writes it to the buffer
 | 
			
		||||
// See https://golang.org/src/mime/quotedprintable/writer.go
 | 
			
		||||
func encode(buf *bytes.Buffer, b byte) {
 | 
			
		||||
	buf.WriteByte('=')
 | 
			
		||||
	buf.WriteByte(lowerhex[b>>4])
 | 
			
		||||
	buf.WriteByte(lowerhex[b&0x0f])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// escape the given alpha character and writes it to the buffer
 | 
			
		||||
func escape(buf *bytes.Buffer, b byte) {
 | 
			
		||||
	buf.WriteByte('_')
 | 
			
		||||
	if b == '_' {
 | 
			
		||||
		buf.WriteByte('_') // another _
 | 
			
		||||
	} else {
 | 
			
		||||
		buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shouldEncode(b byte) bool {
 | 
			
		||||
	return b != '-' && b != '.' && b != '_' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shouldEscape(b byte) bool {
 | 
			
		||||
	return (b >= 'A' && b <= 'Z') || b == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidByte(b byte) bool {
 | 
			
		||||
	return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidEscapedChar(b byte) bool {
 | 
			
		||||
	return b == '_' || (b >= 'a' && b <= 'z')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form.
 | 
			
		||||
// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
 | 
			
		||||
//
 | 
			
		||||
// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z
 | 
			
		||||
// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges
 | 
			
		||||
// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs)
 | 
			
		||||
// and converted to lower-case hex with a leading "=". For example:
 | 
			
		||||
//   Alph@Bet_50up  => _alph=40_bet=5f50up
 | 
			
		||||
func EncodeUserLocalpart(str string) string {
 | 
			
		||||
	strBytes := []byte(str)
 | 
			
		||||
	var outputBuffer bytes.Buffer
 | 
			
		||||
	for _, b := range strBytes {
 | 
			
		||||
		if shouldEncode(b) {
 | 
			
		||||
			encode(&outputBuffer, b)
 | 
			
		||||
		} else if shouldEscape(b) {
 | 
			
		||||
			escape(&outputBuffer, b)
 | 
			
		||||
		} else {
 | 
			
		||||
			outputBuffer.WriteByte(b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return outputBuffer.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeUserLocalpart decodes the given string back into the original input string.
 | 
			
		||||
// Returns an error if the given string is not a valid user ID localpart encoding.
 | 
			
		||||
// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
 | 
			
		||||
//
 | 
			
		||||
// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For
 | 
			
		||||
// example:
 | 
			
		||||
//  _alph=40_bet=5f50up  =>  Alph@Bet_50up
 | 
			
		||||
// Returns an error if the input string contains characters outside the
 | 
			
		||||
// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has
 | 
			
		||||
// an invalid _ escaped byte (e.g. "_5").
 | 
			
		||||
func DecodeUserLocalpart(str string) (string, error) {
 | 
			
		||||
	strBytes := []byte(str)
 | 
			
		||||
	var outputBuffer bytes.Buffer
 | 
			
		||||
	for i := 0; i < len(strBytes); i++ {
 | 
			
		||||
		b := strBytes[i]
 | 
			
		||||
		if !isValidByte(b) {
 | 
			
		||||
			return "", fmt.Errorf("Byte pos %d: Invalid byte", i)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _
 | 
			
		||||
			if i+1 >= len(strBytes) {
 | 
			
		||||
				return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i)
 | 
			
		||||
			}
 | 
			
		||||
			if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping
 | 
			
		||||
				return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i)
 | 
			
		||||
			}
 | 
			
		||||
			if strBytes[i+1] == '_' {
 | 
			
		||||
				outputBuffer.WriteByte('_')
 | 
			
		||||
			} else {
 | 
			
		||||
				outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z
 | 
			
		||||
			}
 | 
			
		||||
			i++ // skip next byte since we just handled it
 | 
			
		||||
		} else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8
 | 
			
		||||
			if i+2 >= len(strBytes) {
 | 
			
		||||
				return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i)
 | 
			
		||||
			}
 | 
			
		||||
			dst := make([]byte, 1)
 | 
			
		||||
			_, err := hex.Decode(dst, strBytes[i+1:i+3])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
			outputBuffer.WriteByte(dst[0])
 | 
			
		||||
			i += 2 // skip next 2 bytes since we just handled it
 | 
			
		||||
		} else { // pass through
 | 
			
		||||
			outputBuffer.WriteByte(b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return outputBuffer.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractUserLocalpart extracts the localpart portion of a user ID.
 | 
			
		||||
// See http://matrix.org/docs/spec/intro.html#user-identifiers
 | 
			
		||||
func ExtractUserLocalpart(userID string) (string, error) {
 | 
			
		||||
	if len(userID) == 0 || userID[0] != '@' {
 | 
			
		||||
		return "", fmt.Errorf("%s is not a valid user id", userID)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimPrefix(
 | 
			
		||||
		strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ]
 | 
			
		||||
		"@", // remove "@" prefix
 | 
			
		||||
	), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +25,6 @@ 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.
 | 
			
		||||
@@ -49,9 +48,6 @@ 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"`
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user