forked from lug/matterbridge
		
	Compare commits
	
		
			29 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					59e55cfbd5 | ||
| 
						 | 
					788d3b32ac | ||
| 
						 | 
					1d414cf2fd | ||
| 
						 | 
					cc3c168162 | ||
| 
						 | 
					1ee6837f0e | ||
| 
						 | 
					27dcea7c5b | ||
| 
						 | 
					dcda7f7b8c | ||
| 
						 | 
					e0cbb69a4f | ||
| 
						 | 
					7ec95f786d | ||
| 
						 | 
					1efe40add5 | ||
| 
						 | 
					cbd73ee313 | ||
| 
						 | 
					34227a7a39 | ||
| 
						 | 
					71cb9b2d1d | ||
| 
						 | 
					cd4c9b194f | ||
| 
						 | 
					98762a0235 | ||
| 
						 | 
					2fd1fd9573 | ||
| 
						 | 
					aff3964078 | ||
| 
						 | 
					2778580397 | ||
| 
						 | 
					962062fe44 | ||
| 
						 | 
					0578b21270 | ||
| 
						 | 
					36a800c3f5 | ||
| 
						 | 
					6d21f84187 | ||
| 
						 | 
					f1e9833310 | ||
| 
						 | 
					46f5acc4f9 | ||
| 
						 | 
					95d4dcaeb3 | ||
| 
						 | 
					64c542e614 | ||
| 
						 | 
					13d081ea80 | ||
| 
						 | 
					c0f9d86287 | ||
| 
						 | 
					bcdecdaa73 | 
@@ -54,7 +54,7 @@ See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
# Installing
 | 
			
		||||
## Binaries
 | 
			
		||||
* Latest stable release [v1.4.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
 | 
			
		||||
@@ -175,7 +175,7 @@ Matterbridge wouldn't exist without these libraries:
 | 
			
		||||
* echo - https://github.com/labstack/echo
 | 
			
		||||
* gitter - https://github.com/sromku/go-gitter
 | 
			
		||||
* gops - https://github.com/google/gops
 | 
			
		||||
* irc - https://github.com/thoj/go-ircevent
 | 
			
		||||
* irc - https://github.com/lrstanley/girc
 | 
			
		||||
* mattermost - https://github.com/mattermost/platform
 | 
			
		||||
* matrix - https://github.com/matrix-org/gomatrix
 | 
			
		||||
* slack - https://github.com/nlopes/slack
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,10 @@ type Message struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FileInfo struct {
 | 
			
		||||
	Name string
 | 
			
		||||
	Data *[]byte
 | 
			
		||||
	Name    string
 | 
			
		||||
	Data    *[]byte
 | 
			
		||||
	Comment string
 | 
			
		||||
	URL     string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChannelInfo struct {
 | 
			
		||||
@@ -58,41 +60,46 @@ type Protocol struct {
 | 
			
		||||
	IgnoreMessages         string // all protocols
 | 
			
		||||
	Jid                    string // xmpp
 | 
			
		||||
	Login                  string // mattermost, matrix
 | 
			
		||||
	Muc                    string // xmpp
 | 
			
		||||
	Name                   string // all protocols
 | 
			
		||||
	Nick                   string // all protocols
 | 
			
		||||
	NickFormatter          string // mattermost, slack
 | 
			
		||||
	NickServNick           string // IRC
 | 
			
		||||
	NickServUsername       string // IRC
 | 
			
		||||
	NickServPassword       string // IRC
 | 
			
		||||
	NicksPerRow            int    // mattermost, slack
 | 
			
		||||
	NoHomeServerSuffix     bool   // matrix
 | 
			
		||||
	NoTLS                  bool   // mattermost
 | 
			
		||||
	Password               string // IRC,mattermost,XMPP,matrix
 | 
			
		||||
	PrefixMessagesWithNick bool   // mattemost, slack
 | 
			
		||||
	Protocol               string //all protocols
 | 
			
		||||
	MessageQueue           int    // IRC, size of message queue for flood control
 | 
			
		||||
	MessageDelay           int    // IRC, time in millisecond to wait between messages
 | 
			
		||||
	MessageLength          int    // IRC, max length of a message allowed
 | 
			
		||||
	MessageFormat          string // telegram
 | 
			
		||||
	RemoteNickFormat       string // all protocols
 | 
			
		||||
	Server                 string // IRC,mattermost,XMPP,discord
 | 
			
		||||
	ShowJoinPart           bool   // all protocols
 | 
			
		||||
	ShowEmbeds             bool   // discord
 | 
			
		||||
	SkipTLSVerify          bool   // IRC, mattermost
 | 
			
		||||
	StripNick              bool   // all protocols
 | 
			
		||||
	Team                   string // mattermost
 | 
			
		||||
	Token                  string // gitter, slack, discord, api
 | 
			
		||||
	URL                    string // mattermost, slack // DEPRECATED
 | 
			
		||||
	UseAPI                 bool   // mattermost, slack
 | 
			
		||||
	UseSASL                bool   // IRC
 | 
			
		||||
	UseTLS                 bool   // IRC
 | 
			
		||||
	UseFirstName           bool   // telegram
 | 
			
		||||
	UseUserName            bool   // discord
 | 
			
		||||
	UseInsecureURL         bool   // telegram
 | 
			
		||||
	WebhookBindAddress     string // mattermost, slack
 | 
			
		||||
	WebhookURL             string // mattermost, slack
 | 
			
		||||
	WebhookUse             string // mattermost, slack, discord
 | 
			
		||||
	MediaServerDownload    string
 | 
			
		||||
	MediaServerUpload      string
 | 
			
		||||
	MessageDelay           int        // IRC, time in millisecond to wait between messages
 | 
			
		||||
	MessageFormat          string     // telegram
 | 
			
		||||
	MessageLength          int        // IRC, max length of a message allowed
 | 
			
		||||
	MessageQueue           int        // IRC, size of message queue for flood control
 | 
			
		||||
	MessageSplit           bool       // IRC, split long messages with newlines on MessageLength instead of clipping
 | 
			
		||||
	Muc                    string     // xmpp
 | 
			
		||||
	Name                   string     // all protocols
 | 
			
		||||
	Nick                   string     // all protocols
 | 
			
		||||
	NickFormatter          string     // mattermost, slack
 | 
			
		||||
	NickServNick           string     // IRC
 | 
			
		||||
	NickServUsername       string     // IRC
 | 
			
		||||
	NickServPassword       string     // IRC
 | 
			
		||||
	NicksPerRow            int        // mattermost, slack
 | 
			
		||||
	NoHomeServerSuffix     bool       // matrix
 | 
			
		||||
	NoTLS                  bool       // mattermost
 | 
			
		||||
	Password               string     // IRC,mattermost,XMPP,matrix
 | 
			
		||||
	PrefixMessagesWithNick bool       // mattemost, slack
 | 
			
		||||
	Protocol               string     // all protocols
 | 
			
		||||
	ReplaceMessages        [][]string // all protocols
 | 
			
		||||
	ReplaceNicks           [][]string // all protocols
 | 
			
		||||
	RemoteNickFormat       string     // all protocols
 | 
			
		||||
	Server                 string     // IRC,mattermost,XMPP,discord
 | 
			
		||||
	ShowJoinPart           bool       // all protocols
 | 
			
		||||
	ShowEmbeds             bool       // discord
 | 
			
		||||
	SkipTLSVerify          bool       // IRC, mattermost
 | 
			
		||||
	StripNick              bool       // all protocols
 | 
			
		||||
	Team                   string     // mattermost
 | 
			
		||||
	Token                  string     // gitter, slack, discord, api
 | 
			
		||||
	URL                    string     // mattermost, slack // DEPRECATED
 | 
			
		||||
	UseAPI                 bool       // mattermost, slack
 | 
			
		||||
	UseSASL                bool       // IRC
 | 
			
		||||
	UseTLS                 bool       // IRC
 | 
			
		||||
	UseFirstName           bool       // telegram
 | 
			
		||||
	UseUserName            bool       // discord
 | 
			
		||||
	UseInsecureURL         bool       // telegram
 | 
			
		||||
	WebhookBindAddress     string     // mattermost, slack
 | 
			
		||||
	WebhookURL             string     // mattermost, slack
 | 
			
		||||
	WebhookUse             string     // mattermost, slack, discord
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ChannelOptions struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -151,11 +151,12 @@ func (b *bdiscord) Send(msg config.Message) (string, error) {
 | 
			
		||||
					fi := f.(config.FileInfo)
 | 
			
		||||
					files := []*discordgo.File{}
 | 
			
		||||
					files = append(files, &discordgo.File{fi.Name, "", bytes.NewReader(*fi.Data)})
 | 
			
		||||
					_, err = b.c.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{Content: msg.Text, Files: files})
 | 
			
		||||
					_, err = b.c.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{Content: msg.Username + fi.Comment, Files: files})
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("file upload failed: %#v", err)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				return "", nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -125,6 +125,23 @@ func (b *Bgitter) Send(msg config.Message) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				_, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
 
 | 
			
		||||
@@ -26,3 +26,15 @@ func DownloadFile(url string) (*[]byte, error) {
 | 
			
		||||
	resp.Body.Close()
 | 
			
		||||
	return &data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SplitStringLength(input string, length int) string {
 | 
			
		||||
	a := []rune(input)
 | 
			
		||||
	str := ""
 | 
			
		||||
	for i, r := range a {
 | 
			
		||||
		str = str + string(r)
 | 
			
		||||
		if i > 0 && (i+1)%length == 0 {
 | 
			
		||||
			str += "\n"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/lrstanley/girc"
 | 
			
		||||
	"github.com/paulrosania/go-charset/charset"
 | 
			
		||||
@@ -81,12 +82,22 @@ func (b *Birc) Connect() error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// fix strict user handling of girc
 | 
			
		||||
	user := b.Config.Nick
 | 
			
		||||
	for !girc.IsValidUser(user) {
 | 
			
		||||
		if len(user) == 1 {
 | 
			
		||||
			user = "matterbridge"
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		user = user[1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := girc.New(girc.Config{
 | 
			
		||||
		Server:     server,
 | 
			
		||||
		ServerPass: b.Config.Password,
 | 
			
		||||
		Port:       port,
 | 
			
		||||
		Nick:       b.Config.Nick,
 | 
			
		||||
		User:       b.Config.Nick,
 | 
			
		||||
		User:       user,
 | 
			
		||||
		Name:       b.Config.Nick,
 | 
			
		||||
		SSL:        b.Config.UseTLS,
 | 
			
		||||
		TLSConfig:  &tls.Config{InsecureSkipVerify: b.Config.SkipTLSVerify, ServerName: server},
 | 
			
		||||
@@ -168,6 +179,23 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
			
		||||
		msg.Text = buf.String()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// split long messages on messageLength, to avoid clipped messages #281
 | 
			
		||||
	if b.Config.MessageSplit {
 | 
			
		||||
		msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength)
 | 
			
		||||
	}
 | 
			
		||||
	for _, text := range strings.Split(msg.Text, "\n") {
 | 
			
		||||
		if len(text) > b.Config.MessageLength {
 | 
			
		||||
			text = text[:b.Config.MessageLength] + " <message clipped>"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,14 @@
 | 
			
		||||
package bmatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"mime"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	matrix "github.com/matrix-org/gomatrix"
 | 
			
		||||
)
 | 
			
		||||
@@ -87,6 +91,43 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
			matrix.TextMessage{"m.emote", msg.Username + msg.Text})
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		// check if we have files to upload (from slack, telegram or mattermost)
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				content := bytes.NewReader(*fi.Data)
 | 
			
		||||
				sp := strings.Split(fi.Name, ".")
 | 
			
		||||
				mtype := mime.TypeByExtension("." + sp[len(sp)-1])
 | 
			
		||||
				if strings.Contains(mtype, "image") ||
 | 
			
		||||
					strings.Contains(mtype, "video") {
 | 
			
		||||
					flog.Debugf("uploading file: %s %s", fi.Name, mtype)
 | 
			
		||||
					res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("file upload failed: %#v", err)
 | 
			
		||||
					}
 | 
			
		||||
					if strings.Contains(mtype, "video") {
 | 
			
		||||
						flog.Debugf("sendVideo %s", res.ContentURI)
 | 
			
		||||
						_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							flog.Errorf("sendVideo failed: %#v", err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if strings.Contains(mtype, "image") {
 | 
			
		||||
						flog.Debugf("sendImage %s", res.ContentURI)
 | 
			
		||||
						_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							flog.Errorf("sendImage failed: %#v", err)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					flog.Debugf("result: %#v", res)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
@@ -104,7 +145,13 @@ func (b *Bmatrix) getRoomID(channel string) string {
 | 
			
		||||
func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
	syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
 | 
			
		||||
	syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
 | 
			
		||||
		if (ev.Content["msgtype"].(string) == "m.text" || ev.Content["msgtype"].(string) == "m.notice" || ev.Content["msgtype"].(string) == "m.emote") && ev.Sender != b.UserID {
 | 
			
		||||
		flog.Debugf("Received: %#v", ev)
 | 
			
		||||
		if (ev.Content["msgtype"].(string) == "m.text" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.notice" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.emote" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.file" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.video") && ev.Sender != b.UserID {
 | 
			
		||||
			b.RLock()
 | 
			
		||||
			channel, ok := b.RoomMap[ev.RoomID]
 | 
			
		||||
			b.RUnlock()
 | 
			
		||||
@@ -121,10 +168,31 @@ func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
			if ev.Content["msgtype"].(string) == "m.emote" {
 | 
			
		||||
				rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
			}
 | 
			
		||||
			if ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
				ev.Content["msgtype"].(string) == "m.video" ||
 | 
			
		||||
				ev.Content["msgtype"].(string) == "m.file" {
 | 
			
		||||
				flog.Debugf("ev: %#v", ev)
 | 
			
		||||
				rmsg.Extra = make(map[string][]interface{})
 | 
			
		||||
				url := ev.Content["url"].(string)
 | 
			
		||||
				url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1)
 | 
			
		||||
				info := ev.Content["info"].(map[string]interface{})
 | 
			
		||||
				size := info["size"].(float64)
 | 
			
		||||
				name := ev.Content["body"].(string)
 | 
			
		||||
				flog.Debugf("trying to download %#v with size %#v", name, size)
 | 
			
		||||
				if size <= 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
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debugf("Received: %#v", ev)
 | 
			
		||||
	})
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
 
 | 
			
		||||
@@ -190,9 +190,9 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
 | 
			
		||||
					flog.Debugf("ERROR %#v", err)
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
				message = "uploaded a file: " + fi.Name
 | 
			
		||||
				message = fi.Comment
 | 
			
		||||
				if b.Config.PrefixMessagesWithNick {
 | 
			
		||||
					message = nick + "uploaded a file: " + fi.Name
 | 
			
		||||
					message = nick + fi.Comment
 | 
			
		||||
				}
 | 
			
		||||
				res, err = b.mc.PostMessageWithFiles(b.mc.GetChannelId(channel, ""), message, []string{id})
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -195,9 +195,10 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				_, err = b.sc.UploadFile(slack.FileUploadParameters{
 | 
			
		||||
					Reader:   bytes.NewReader(*fi.Data),
 | 
			
		||||
					Filename: fi.Name,
 | 
			
		||||
					Channels: []string{schannel.ID},
 | 
			
		||||
					Reader:         bytes.NewReader(*fi.Data),
 | 
			
		||||
					Filename:       fi.Name,
 | 
			
		||||
					Channels:       []string{schannel.ID},
 | 
			
		||||
					InitialComment: fi.Comment,
 | 
			
		||||
				})
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					flog.Errorf("uploadfile %#v", err)
 | 
			
		||||
@@ -294,11 +295,16 @@ func (b *Bslack) handleSlack() {
 | 
			
		||||
		if message.Raw.File != nil {
 | 
			
		||||
			// limit to 1MB for now
 | 
			
		||||
			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 {
 | 
			
		||||
					msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{Name: message.Raw.File.Name, Data: data})
 | 
			
		||||
					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})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package btelegram
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
@@ -113,20 +114,14 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Errorf("file upload failed: %#v", err)
 | 
			
		||||
				}
 | 
			
		||||
				if fi.Comment != "" {
 | 
			
		||||
					b.sendMessage(chatid, msg.Username+fi.Comment)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m := tgbotapi.NewMessage(chatid, msg.Username+msg.Text)
 | 
			
		||||
	if b.Config.MessageFormat == "HTML" {
 | 
			
		||||
		m.ParseMode = tgbotapi.ModeHTML
 | 
			
		||||
	}
 | 
			
		||||
	res, err := b.c.Send(m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return strconv.Itoa(res.MessageID), nil
 | 
			
		||||
 | 
			
		||||
	return b.sendMessage(chatid, msg.Username+msg.Text)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
@@ -178,12 +173,11 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
		if message.Video != nil {
 | 
			
		||||
			b.handleDownload(message.Video, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Photo != nil && b.Config.UseInsecureURL {
 | 
			
		||||
		if message.Photo != nil {
 | 
			
		||||
			b.handleDownload(message.Photo, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
		if message.Document != nil && b.Config.UseInsecureURL {
 | 
			
		||||
			b.handleDownload(message.Sticker, &fmsg)
 | 
			
		||||
			text = text + " " + message.Document.FileName + " : " + b.getFileDirectURL(message.Document.FileID)
 | 
			
		||||
		if message.Document != nil {
 | 
			
		||||
			b.handleDownload(message.Document, &fmsg)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// quote the previous message
 | 
			
		||||
@@ -208,7 +202,7 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
 | 
			
		||||
 | 
			
		||||
		if text != "" || len(fmsg.Extra) > 0 {
 | 
			
		||||
			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)}
 | 
			
		||||
			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
 | 
			
		||||
		}
 | 
			
		||||
@@ -228,28 +222,38 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	url := ""
 | 
			
		||||
	name := ""
 | 
			
		||||
	text := ""
 | 
			
		||||
	fileid := ""
 | 
			
		||||
	switch v := file.(type) {
 | 
			
		||||
	case *tgbotapi.Sticker:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		name = "sticker"
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		if !strings.HasSuffix(name, ".webp") {
 | 
			
		||||
			name = name + ".webp"
 | 
			
		||||
		}
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *tgbotapi.Video:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		name = "video"
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		text = " " + url
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	case *[]tgbotapi.PhotoSize:
 | 
			
		||||
		photos := *v
 | 
			
		||||
		size = photos[len(photos)-1].FileSize
 | 
			
		||||
		url = b.getFileDirectURL(photos[len(photos)-1].FileID)
 | 
			
		||||
		name = "photo"
 | 
			
		||||
		urlPart := strings.Split(url, "/")
 | 
			
		||||
		name = urlPart[len(urlPart)-1]
 | 
			
		||||
		text = " " + url
 | 
			
		||||
	case *tgbotapi.Document:
 | 
			
		||||
		size = v.FileSize
 | 
			
		||||
		url = b.getFileDirectURL(v.FileID)
 | 
			
		||||
		name = v.FileName
 | 
			
		||||
		text = " " + v.FileName + " : " + url
 | 
			
		||||
		fileid = v.FileID
 | 
			
		||||
	}
 | 
			
		||||
	if b.Config.UseInsecureURL {
 | 
			
		||||
		msg.Text = text
 | 
			
		||||
@@ -257,12 +261,26 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
 | 
			
		||||
	}
 | 
			
		||||
	// if we have a file attached, download it (in memory) and put a pointer to it in msg.Extra
 | 
			
		||||
	// limit to 1MB for now
 | 
			
		||||
	flog.Debugf("trying to download %#v fileid %#v with size %#v", name, fileid, size)
 | 
			
		||||
	if size <= 1000000 {
 | 
			
		||||
		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})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Btelegram) sendMessage(chatid int64, text string) (string, error) {
 | 
			
		||||
	m := tgbotapi.NewMessage(chatid, text)
 | 
			
		||||
	if b.Config.MessageFormat == "HTML" {
 | 
			
		||||
		m.ParseMode = tgbotapi.ModeHTML
 | 
			
		||||
	}
 | 
			
		||||
	res, err := b.c.Send(m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return strconv.Itoa(res.MessageID), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -85,6 +85,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
			for _, f := range msg.Extra["file"] {
 | 
			
		||||
				fi := f.(config.FileInfo)
 | 
			
		||||
				if fi.URL != "" {
 | 
			
		||||
					msg.Text = fi.URL
 | 
			
		||||
				}
 | 
			
		||||
				b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
			
		||||
			}
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,23 @@
 | 
			
		||||
# v1.5.0
 | 
			
		||||
## New features
 | 
			
		||||
* general: remote mediaserver support. See MediaServerDownload and MediaServerUpload in matterbridge.toml.sample
 | 
			
		||||
  more information on https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
 | 
			
		||||
* general: Add support for ReplaceNicks using regexp to replace nicks. Closes #269 (see matterbridge.toml.sample)
 | 
			
		||||
* general: Add support for ReplaceMessages using regexp to replace messages. #269 (see matterbridge.toml.sample)
 | 
			
		||||
* irc: Add MessageSplit option to split messages on MessageLength (irc). Closes #281
 | 
			
		||||
* matrix: Add support for uploading images/video (matrix). Closes #302
 | 
			
		||||
* matrix: Add support for uploaded images/video (matrix) 
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: Add webp extension to stickers if necessary (telegram)
 | 
			
		||||
* mattermost: Break when re-login fails (mattermost)
 | 
			
		||||
 | 
			
		||||
# v1.4.1
 | 
			
		||||
## Bugfix
 | 
			
		||||
* telegram: fix issue with uploading for images/documents/stickers
 | 
			
		||||
* slack: remove double messages sent to other bridges when uploading files
 | 
			
		||||
* irc: Fix strict user handling of girc (irc). Closes #298 
 | 
			
		||||
 | 
			
		||||
# v1.4.0
 | 
			
		||||
## Breaking changes
 | 
			
		||||
* general: `[general]` settings don't override the specific bridge settings
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
package gateway
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	//	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"github.com/hashicorp/golang-lru"
 | 
			
		||||
	"github.com/peterhellberg/emojilib"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -152,7 +155,11 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
	// only slack now, check will have to be done in the different bridges.
 | 
			
		||||
	// we need to check if we can't use fallback or text in other bridges
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		if dest.Protocol != "slack" {
 | 
			
		||||
		if dest.Protocol != "discord" &&
 | 
			
		||||
			dest.Protocol != "slack" &&
 | 
			
		||||
			dest.Protocol != "mattermost" &&
 | 
			
		||||
			dest.Protocol != "telegram" &&
 | 
			
		||||
			dest.Protocol != "matrix" {
 | 
			
		||||
			if msg.Text == "" {
 | 
			
		||||
				return brMsgIDs
 | 
			
		||||
			}
 | 
			
		||||
@@ -210,8 +217,8 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if msg.Text == "" {
 | 
			
		||||
		// we have an attachment
 | 
			
		||||
		if msg.Extra != nil && msg.Extra["attachments"] != nil {
 | 
			
		||||
		// we have an attachment or actual bytes
 | 
			
		||||
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
 | 
			
		||||
@@ -251,6 +258,20 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
 | 
			
		||||
	if nick == "" {
 | 
			
		||||
		nick = gw.Config.General.RemoteNickFormat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// loop to replace nicks
 | 
			
		||||
	for _, outer := range br.Config.ReplaceNicks {
 | 
			
		||||
		search := outer[0]
 | 
			
		||||
		replace := outer[1]
 | 
			
		||||
		// TODO move compile to bridge init somewhere
 | 
			
		||||
		re, err := regexp.Compile(search)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		msg.Username = re.ReplaceAllString(msg.Username, replace)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(msg.Username) > 0 {
 | 
			
		||||
		// fix utf-8 issue #193
 | 
			
		||||
		i := 0
 | 
			
		||||
@@ -284,9 +305,50 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string
 | 
			
		||||
func (gw *Gateway) modifyMessage(msg *config.Message) {
 | 
			
		||||
	// replace :emoji: to unicode
 | 
			
		||||
	msg.Text = emojilib.Replace(msg.Text)
 | 
			
		||||
	br := gw.Bridges[msg.Account]
 | 
			
		||||
	// loop to replace messages
 | 
			
		||||
	for _, outer := range br.Config.ReplaceMessages {
 | 
			
		||||
		search := outer[0]
 | 
			
		||||
		replace := outer[1]
 | 
			
		||||
		// TODO move compile to bridge init somewhere
 | 
			
		||||
		re, err := regexp.Compile(search)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		msg.Text = re.ReplaceAllString(msg.Text, replace)
 | 
			
		||||
	}
 | 
			
		||||
	msg.Gateway = gw.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
			
		||||
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(msg.Extra["file"]) > 0 {
 | 
			
		||||
		client := &http.Client{
 | 
			
		||||
			Timeout: time.Second * 5,
 | 
			
		||||
		}
 | 
			
		||||
		for i, f := range msg.Extra["file"] {
 | 
			
		||||
			fi := f.(config.FileInfo)
 | 
			
		||||
			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
 | 
			
		||||
			reader := bytes.NewReader(*fi.Data)
 | 
			
		||||
			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
 | 
			
		||||
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
 | 
			
		||||
			extra := msg.Extra["file"][i].(config.FileInfo)
 | 
			
		||||
			extra.URL = durl
 | 
			
		||||
			msg.Extra["file"][i] = extra
 | 
			
		||||
			req, _ := http.NewRequest("PUT", url, reader)
 | 
			
		||||
			req.Header.Set("Content-Type", "binary/octet-stream")
 | 
			
		||||
			_, err := client.Do(req)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Errorf("mediaserver upload failed: %#v", err)
 | 
			
		||||
			}
 | 
			
		||||
			log.Debugf("mediaserver download URL = %s", durl)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getChannelID(msg config.Message) string {
 | 
			
		||||
	return msg.Channel + msg.Account
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,7 @@ func (r *Router) handleReceive() {
 | 
			
		||||
			if !gw.ignoreMessage(&msg) {
 | 
			
		||||
				msg.Timestamp = time.Now()
 | 
			
		||||
				gw.modifyMessage(&msg)
 | 
			
		||||
				gw.handleFiles(&msg)
 | 
			
		||||
				for _, br := range gw.Bridges {
 | 
			
		||||
					msgIDs = append(msgIDs, gw.handleMessage(msg, br)...)
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.4.0"
 | 
			
		||||
	version = "1.5.0"
 | 
			
		||||
	githash string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,11 @@ MessageQueue=30
 | 
			
		||||
#OPTIONAL (default 400)
 | 
			
		||||
MessageLength=400
 | 
			
		||||
 | 
			
		||||
#Split messages on MessageLength instead of showing the <message clipped>
 | 
			
		||||
#WARNING: this could lead to flooding
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
MessageSplit=false
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore. 
 | 
			
		||||
#Messages from those users will not be sent to other bridges.
 | 
			
		||||
#OPTIONAL
 | 
			
		||||
@@ -91,6 +96,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -154,6 +176,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#Messages you want to replace.
 | 
			
		||||
#It replaces outgoing messages from the bridge.
 | 
			
		||||
#So you need to place it by the sending bridge definition.
 | 
			
		||||
#Regular expressions supported
 | 
			
		||||
#Some examples:
 | 
			
		||||
#This replaces cat => dog and sleep => awake
 | 
			
		||||
#ReplaceMessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#This Replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#ReplaceMessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#Nicks you want to replace.
 | 
			
		||||
#See ReplaceMessages for syntaxA
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -208,6 +247,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -322,6 +378,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -366,6 +439,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -457,6 +547,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -525,6 +632,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -592,6 +716,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -660,6 +801,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -720,6 +878,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -774,6 +949,23 @@ IgnoreNicks="spammer1 spammer2"
 | 
			
		||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
			
		||||
IgnoreMessages="^~~ badword"
 | 
			
		||||
 | 
			
		||||
#messages you want to replace.
 | 
			
		||||
#it replaces outgoing messages from the bridge.
 | 
			
		||||
#so you need to place it by the sending bridge definition.
 | 
			
		||||
#regular expressions supported
 | 
			
		||||
#some examples:
 | 
			
		||||
#this replaces cat => dog and sleep => awake
 | 
			
		||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
			
		||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
			
		||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
			
		||||
 | 
			
		||||
#nicks you want to replace.
 | 
			
		||||
#see replacemessages for syntaxa
 | 
			
		||||
#optional (default empty)
 | 
			
		||||
ReplaceNicks=[ ["user--","user"] ]
 | 
			
		||||
 | 
			
		||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
			
		||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
 | 
			
		||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
 | 
			
		||||
@@ -838,6 +1030,21 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripNick=false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#MediaServerUpload and MediaServerDownload are used for uploading images/files/video to
 | 
			
		||||
#a remote "mediaserver" (a webserver like caddy for example).
 | 
			
		||||
#When configured images/files uploaded on bridges like mattermost,slack, telegram will be downloaded
 | 
			
		||||
#and uploaded again to MediaServerUpload URL
 | 
			
		||||
#The MediaServerDownload will be used so that bridges without native uploading support:
 | 
			
		||||
#gitter, irc and xmpp will be shown links to the files on MediaServerDownload
 | 
			
		||||
#
 | 
			
		||||
#More information https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
MediaServerUpload="https://user:pass@yourserver.com/upload"
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
MediaServerDownload="https://youserver.com/download"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#Gateway configuration
 | 
			
		||||
###################################################################
 | 
			
		||||
 
 | 
			
		||||
@@ -817,9 +817,14 @@ func (m *MMClient) StatusLoop() {
 | 
			
		||||
				backoff = time.Second * 60
 | 
			
		||||
			case <-time.After(time.Second * 5):
 | 
			
		||||
				if retries > 3 {
 | 
			
		||||
					m.log.Debug("StatusLoop() timeout")
 | 
			
		||||
					m.Logout()
 | 
			
		||||
					m.WsQuit = false
 | 
			
		||||
					m.Login()
 | 
			
		||||
					err := m.Login()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						log.Errorf("Login failed: %#v", err)
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					if m.OnWsConnect != nil {
 | 
			
		||||
						m.OnWsConnect()
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										80
									
								
								vendor/github.com/lrstanley/girc/builtin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/lrstanley/girc/builtin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,64 +16,62 @@ func (c *Client) registerBuiltins() {
 | 
			
		||||
	c.Handlers.mu.Lock()
 | 
			
		||||
 | 
			
		||||
	// Built-in things that should always be supported.
 | 
			
		||||
	c.Handlers.register(true, RPL_WELCOME, HandlerFunc(func(c *Client, e Event) {
 | 
			
		||||
		go handleConnect(c, e)
 | 
			
		||||
	}))
 | 
			
		||||
	c.Handlers.register(true, PING, HandlerFunc(handlePING))
 | 
			
		||||
	c.Handlers.register(true, PONG, HandlerFunc(handlePONG))
 | 
			
		||||
	c.Handlers.register(true, true, RPL_WELCOME, HandlerFunc(handleConnect))
 | 
			
		||||
	c.Handlers.register(true, false, PING, HandlerFunc(handlePING))
 | 
			
		||||
	c.Handlers.register(true, false, PONG, HandlerFunc(handlePONG))
 | 
			
		||||
 | 
			
		||||
	if !c.Config.disableTracking {
 | 
			
		||||
		// Joins/parts/anything that may add/remove/rename users.
 | 
			
		||||
		c.Handlers.register(true, JOIN, HandlerFunc(handleJOIN))
 | 
			
		||||
		c.Handlers.register(true, PART, HandlerFunc(handlePART))
 | 
			
		||||
		c.Handlers.register(true, KICK, HandlerFunc(handleKICK))
 | 
			
		||||
		c.Handlers.register(true, QUIT, HandlerFunc(handleQUIT))
 | 
			
		||||
		c.Handlers.register(true, NICK, HandlerFunc(handleNICK))
 | 
			
		||||
		c.Handlers.register(true, RPL_NAMREPLY, HandlerFunc(handleNAMES))
 | 
			
		||||
		c.Handlers.register(true, false, JOIN, HandlerFunc(handleJOIN))
 | 
			
		||||
		c.Handlers.register(true, false, PART, HandlerFunc(handlePART))
 | 
			
		||||
		c.Handlers.register(true, false, KICK, HandlerFunc(handleKICK))
 | 
			
		||||
		c.Handlers.register(true, false, QUIT, HandlerFunc(handleQUIT))
 | 
			
		||||
		c.Handlers.register(true, false, NICK, HandlerFunc(handleNICK))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_NAMREPLY, HandlerFunc(handleNAMES))
 | 
			
		||||
 | 
			
		||||
		// Modes.
 | 
			
		||||
		c.Handlers.register(true, MODE, HandlerFunc(handleMODE))
 | 
			
		||||
		c.Handlers.register(true, RPL_CHANNELMODEIS, HandlerFunc(handleMODE))
 | 
			
		||||
		c.Handlers.register(true, false, MODE, HandlerFunc(handleMODE))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_CHANNELMODEIS, HandlerFunc(handleMODE))
 | 
			
		||||
 | 
			
		||||
		// WHO/WHOX responses.
 | 
			
		||||
		c.Handlers.register(true, RPL_WHOREPLY, HandlerFunc(handleWHO))
 | 
			
		||||
		c.Handlers.register(true, RPL_WHOSPCRPL, HandlerFunc(handleWHO))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_WHOREPLY, HandlerFunc(handleWHO))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_WHOSPCRPL, HandlerFunc(handleWHO))
 | 
			
		||||
 | 
			
		||||
		// Other misc. useful stuff.
 | 
			
		||||
		c.Handlers.register(true, TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, RPL_TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, RPL_MYINFO, HandlerFunc(handleMYINFO))
 | 
			
		||||
		c.Handlers.register(true, RPL_ISUPPORT, HandlerFunc(handleISUPPORT))
 | 
			
		||||
		c.Handlers.register(true, RPL_MOTDSTART, HandlerFunc(handleMOTD))
 | 
			
		||||
		c.Handlers.register(true, RPL_MOTD, HandlerFunc(handleMOTD))
 | 
			
		||||
		c.Handlers.register(true, false, TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_TOPIC, HandlerFunc(handleTOPIC))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_MYINFO, HandlerFunc(handleMYINFO))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_ISUPPORT, HandlerFunc(handleISUPPORT))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_MOTDSTART, HandlerFunc(handleMOTD))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_MOTD, HandlerFunc(handleMOTD))
 | 
			
		||||
 | 
			
		||||
		// Keep users lastactive times up to date.
 | 
			
		||||
		c.Handlers.register(true, PRIVMSG, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, NOTICE, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, TOPIC, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, KICK, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, PRIVMSG, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, NOTICE, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, TOPIC, HandlerFunc(updateLastActive))
 | 
			
		||||
		c.Handlers.register(true, false, KICK, HandlerFunc(updateLastActive))
 | 
			
		||||
 | 
			
		||||
		// CAP IRCv3-specific tracking and functionality.
 | 
			
		||||
		c.Handlers.register(true, CAP, HandlerFunc(handleCAP))
 | 
			
		||||
		c.Handlers.register(true, CAP_CHGHOST, HandlerFunc(handleCHGHOST))
 | 
			
		||||
		c.Handlers.register(true, CAP_AWAY, HandlerFunc(handleAWAY))
 | 
			
		||||
		c.Handlers.register(true, CAP_ACCOUNT, HandlerFunc(handleACCOUNT))
 | 
			
		||||
		c.Handlers.register(true, ALL_EVENTS, HandlerFunc(handleTags))
 | 
			
		||||
		c.Handlers.register(true, false, CAP, HandlerFunc(handleCAP))
 | 
			
		||||
		c.Handlers.register(true, false, CAP_CHGHOST, HandlerFunc(handleCHGHOST))
 | 
			
		||||
		c.Handlers.register(true, false, CAP_AWAY, HandlerFunc(handleAWAY))
 | 
			
		||||
		c.Handlers.register(true, false, CAP_ACCOUNT, HandlerFunc(handleACCOUNT))
 | 
			
		||||
		c.Handlers.register(true, false, ALL_EVENTS, HandlerFunc(handleTags))
 | 
			
		||||
 | 
			
		||||
		// SASL IRCv3 support.
 | 
			
		||||
		c.Handlers.register(true, AUTHENTICATE, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, RPL_SASLSUCCESS, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, RPL_NICKLOCKED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, ERR_SASLFAIL, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, ERR_SASLTOOLONG, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, ERR_SASLABORTED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, RPL_SASLMECHS, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, AUTHENTICATE, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_SASLSUCCESS, HandlerFunc(handleSASL))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_NICKLOCKED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, ERR_SASLFAIL, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, ERR_SASLTOOLONG, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, ERR_SASLABORTED, HandlerFunc(handleSASLError))
 | 
			
		||||
		c.Handlers.register(true, false, RPL_SASLMECHS, HandlerFunc(handleSASLError))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Nickname collisions.
 | 
			
		||||
	c.Handlers.register(true, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, false, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, false, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
	c.Handlers.register(true, false, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler))
 | 
			
		||||
 | 
			
		||||
	c.Handlers.mu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
@@ -389,7 +387,7 @@ func handleISUPPORT(c *Client, e Event) {
 | 
			
		||||
	c.state.Lock()
 | 
			
		||||
	// Skip the first parameter, as it's our nickname.
 | 
			
		||||
	for i := 1; i < len(e.Params); i++ {
 | 
			
		||||
		j := strings.IndexByte(e.Params[i], 0x3D) // =
 | 
			
		||||
		j := strings.IndexByte(e.Params[i], '=')
 | 
			
		||||
 | 
			
		||||
		if j < 1 || (j+1) == len(e.Params[i]) {
 | 
			
		||||
			c.state.serverOptions[e.Params[i]] = ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								vendor/github.com/lrstanley/girc/cap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/lrstanley/girc/cap.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -136,7 +136,7 @@ func handleCAP(c *Client, e Event) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Let them know which ones we'd like to enable.
 | 
			
		||||
			c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")})
 | 
			
		||||
			c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " "), EmptyTrailing: true})
 | 
			
		||||
 | 
			
		||||
			// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests
 | 
			
		||||
			// due to cap-notify, we can re-evaluate what we can support.
 | 
			
		||||
@@ -375,11 +375,11 @@ func handleTags(c *Client, e Event) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	prefixTag      byte = 0x40 // @
 | 
			
		||||
	prefixTagValue byte = 0x3D // =
 | 
			
		||||
	prefixUserTag  byte = 0x2B // +
 | 
			
		||||
	tagSeparator   byte = 0x3B // ;
 | 
			
		||||
	maxTagLength   int  = 511  // 510 + @ and " " (space), though space usually not included.
 | 
			
		||||
	prefixTag      byte = '@'
 | 
			
		||||
	prefixTagValue byte = '='
 | 
			
		||||
	prefixUserTag  byte = '+'
 | 
			
		||||
	tagSeparator   byte = ';'
 | 
			
		||||
	maxTagLength   int  = 511 // 510 + @ and " " (space), though space usually not included.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Tags represents the key-value pairs in IRCv3 message tags. The map contains
 | 
			
		||||
@@ -618,7 +618,7 @@ func validTag(name string) bool {
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(name); i++ {
 | 
			
		||||
		// A-Z, a-z, 0-9, -/._
 | 
			
		||||
		if (name[i] < 0x41 || name[i] > 0x5A) && (name[i] < 0x61 || name[i] > 0x7A) && (name[i] < 0x2D || name[i] > 0x39) && name[i] != 0x5F {
 | 
			
		||||
		if (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '-' || name[i] > '9') && name[i] != '_' {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -631,7 +631,7 @@ func validTag(name string) bool {
 | 
			
		||||
func validTagValue(value string) bool {
 | 
			
		||||
	for i := 0; i < len(value); i++ {
 | 
			
		||||
		// Don't allow any invisible chars within the tag, or semicolons.
 | 
			
		||||
		if value[i] < 0x21 || value[i] > 0x7E || value[i] == 0x3B {
 | 
			
		||||
		if value[i] < '!' || value[i] > '~' || value[i] == ';' {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								vendor/github.com/lrstanley/girc/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/lrstanley/girc/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -191,18 +191,6 @@ func (conf *Config) isValid() error {
 | 
			
		||||
// connected.
 | 
			
		||||
var ErrNotConnected = errors.New("client is not connected to server")
 | 
			
		||||
 | 
			
		||||
// ErrDisconnected is called when Config.Retries is less than 1, and we
 | 
			
		||||
// non-intentionally disconnected from the server.
 | 
			
		||||
var ErrDisconnected = errors.New("unexpectedly disconnected")
 | 
			
		||||
 | 
			
		||||
// ErrInvalidTarget should be returned if the target which you are
 | 
			
		||||
// attempting to send an event to is invalid or doesn't match RFC spec.
 | 
			
		||||
type ErrInvalidTarget struct {
 | 
			
		||||
	Target string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *ErrInvalidTarget) Error() string { return "invalid target: " + e.Target }
 | 
			
		||||
 | 
			
		||||
// New creates a new IRC client with the specified server, name and config.
 | 
			
		||||
func New(config Config) *Client {
 | 
			
		||||
	c := &Client{
 | 
			
		||||
@@ -253,6 +241,37 @@ func (c *Client) String() string {
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TLSConnectionState returns the TLS connection state from tls.Conn{}, which
 | 
			
		||||
// is useful to return needed TLS fingerprint info, certificates, verify cert
 | 
			
		||||
// expiration dates, etc. Will only return an error if the underlying
 | 
			
		||||
// connection wasn't established using TLS (see ErrConnNotTLS), or if the
 | 
			
		||||
// client isn't connected.
 | 
			
		||||
func (c *Client) TLSConnectionState() (*tls.ConnectionState, error) {
 | 
			
		||||
	c.mu.RLock()
 | 
			
		||||
	defer c.mu.RUnlock()
 | 
			
		||||
	if c.conn == nil {
 | 
			
		||||
		return nil, ErrNotConnected
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.conn.mu.RLock()
 | 
			
		||||
	defer c.conn.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if !c.conn.connected {
 | 
			
		||||
		return nil, ErrNotConnected
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tlsConn, ok := c.conn.sock.(*tls.Conn); ok {
 | 
			
		||||
		cs := tlsConn.ConnectionState()
 | 
			
		||||
		return &cs, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, ErrConnNotTLS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrConnNotTLS is returned when Client.TLSConnectionState() is called, and
 | 
			
		||||
// the connection to the server wasn't made with TLS.
 | 
			
		||||
var ErrConnNotTLS = errors.New("underlying connection is not tls")
 | 
			
		||||
 | 
			
		||||
// Close closes the network connection to the server, and sends a STOPPED
 | 
			
		||||
// event. This should cause Connect() to return with nil. This should be
 | 
			
		||||
// safe to call multiple times. See Connect()'s documentation on how
 | 
			
		||||
@@ -387,7 +406,7 @@ func (c *Client) ConnSince() (since *time.Duration, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsConnected returns true if the client is connected to the server.
 | 
			
		||||
func (c *Client) IsConnected() (connected bool) {
 | 
			
		||||
func (c *Client) IsConnected() bool {
 | 
			
		||||
	c.mu.RLock()
 | 
			
		||||
	if c.conn == nil {
 | 
			
		||||
		c.mu.RUnlock()
 | 
			
		||||
@@ -395,7 +414,7 @@ func (c *Client) IsConnected() (connected bool) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.conn.mu.RLock()
 | 
			
		||||
	connected = c.conn.connected
 | 
			
		||||
	connected := c.conn.connected
 | 
			
		||||
	c.conn.mu.RUnlock()
 | 
			
		||||
	c.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
@@ -445,9 +464,9 @@ func (c *Client) GetHost() string {
 | 
			
		||||
	return c.state.host
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Channels returns the active list of channels that the client is in.
 | 
			
		||||
// ChannelList returns the active list of channel names that the client is in.
 | 
			
		||||
// Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) Channels() []string {
 | 
			
		||||
func (c *Client) ChannelList() []string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
@@ -463,9 +482,26 @@ func (c *Client) Channels() []string {
 | 
			
		||||
	return channels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Users returns the active list of users that the client is tracking across
 | 
			
		||||
// all files. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) Users() []string {
 | 
			
		||||
// Channels returns the active channels that the client is in. Panics if
 | 
			
		||||
// tracking is disabled.
 | 
			
		||||
func (c *Client) Channels() []*Channel {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
	channels := make([]*Channel, len(c.state.channels))
 | 
			
		||||
	var i int
 | 
			
		||||
	for channel := range c.state.channels {
 | 
			
		||||
		channels[i] = c.state.channels[channel].Copy()
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	c.state.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return channels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserList returns the active list of nicknames that the client is tracking
 | 
			
		||||
// across all networks. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) UserList() []string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
@@ -481,6 +517,23 @@ func (c *Client) Users() []string {
 | 
			
		||||
	return users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Users returns the active users that the client is tracking across all
 | 
			
		||||
// networks. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) Users() []*User {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
	users := make([]*User, len(c.state.users))
 | 
			
		||||
	var i int
 | 
			
		||||
	for user := range c.state.users {
 | 
			
		||||
		users[i] = c.state.users[user].Copy()
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	c.state.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LookupChannel looks up a given channel in state. If the channel doesn't
 | 
			
		||||
// exist, nil is returned. Panics if tracking is disabled.
 | 
			
		||||
func (c *Client) LookupChannel(name string) *Channel {
 | 
			
		||||
@@ -562,30 +615,30 @@ func (c *Client) NetworkName() (name string) {
 | 
			
		||||
// supplied this information during connection. May be empty if the server
 | 
			
		||||
// does not support RPL_MYINFO. Will panic if used when tracking has been
 | 
			
		||||
// disabled.
 | 
			
		||||
func (c *Client) ServerVersion() (version string) {
 | 
			
		||||
func (c *Client) ServerVersion() string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	version, _ = c.GetServerOption("VERSION")
 | 
			
		||||
	version, _ := c.GetServerOption("VERSION")
 | 
			
		||||
 | 
			
		||||
	return version
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerMOTD returns the servers message of the day, if the server has sent
 | 
			
		||||
// it upon connect. Will panic if used when tracking has been disabled.
 | 
			
		||||
func (c *Client) ServerMOTD() (motd string) {
 | 
			
		||||
func (c *Client) ServerMOTD() string {
 | 
			
		||||
	c.panicIfNotTracking()
 | 
			
		||||
 | 
			
		||||
	c.state.RLock()
 | 
			
		||||
	motd = c.state.motd
 | 
			
		||||
	motd := c.state.motd
 | 
			
		||||
	c.state.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return motd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Lag is the latency between the server and the client. This is measured by
 | 
			
		||||
// determining the difference in time between when we ping the server, and
 | 
			
		||||
// Latency is the latency between the server and the client. This is measured
 | 
			
		||||
// by determining the difference in time between when we ping the server, and
 | 
			
		||||
// when we receive a pong.
 | 
			
		||||
func (c *Client) Lag() time.Duration {
 | 
			
		||||
func (c *Client) Latency() time.Duration {
 | 
			
		||||
	c.mu.RLock()
 | 
			
		||||
	c.conn.mu.RLock()
 | 
			
		||||
	delta := c.conn.lastPong.Sub(c.conn.lastPing)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,8 +12,9 @@ import (
 | 
			
		||||
 | 
			
		||||
// Input is a wrapper for events, based around private messages.
 | 
			
		||||
type Input struct {
 | 
			
		||||
	Origin *girc.Event
 | 
			
		||||
	Args   []string
 | 
			
		||||
	Origin  *girc.Event
 | 
			
		||||
	Args    []string
 | 
			
		||||
	RawArgs string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Command is an IRC command, supporting aliases, help documentation and easy
 | 
			
		||||
@@ -189,8 +190,9 @@ func (ch *CmdHandler) Execute(client *girc.Client, event girc.Event) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	in := &Input{
 | 
			
		||||
		Origin: &event,
 | 
			
		||||
		Args:   args,
 | 
			
		||||
		Origin:  &event,
 | 
			
		||||
		Args:    args,
 | 
			
		||||
		RawArgs: parsed[2],
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go cmd.Fn(client, in)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										261
									
								
								vendor/github.com/lrstanley/girc/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										261
									
								
								vendor/github.com/lrstanley/girc/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -16,18 +16,13 @@ type Commands struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Nick changes the client nickname.
 | 
			
		||||
func (cmd *Commands) Nick(name string) error {
 | 
			
		||||
	if !IsValidNick(name) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: name}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) Nick(name string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: NICK, Params: []string{name}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Join attempts to enter a list of IRC channels, at bulk if possible to
 | 
			
		||||
// prevent sending extensive JOIN commands.
 | 
			
		||||
func (cmd *Commands) Join(channels ...string) error {
 | 
			
		||||
func (cmd *Commands) Join(channels ...string) {
 | 
			
		||||
	// We can join multiple channels at once, however we need to ensure that
 | 
			
		||||
	// we are not exceeding the line length. (see maxLength)
 | 
			
		||||
	max := maxLength - len(JOIN) - 1
 | 
			
		||||
@@ -35,10 +30,6 @@ func (cmd *Commands) Join(channels ...string) error {
 | 
			
		||||
	var buffer string
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(channels); i++ {
 | 
			
		||||
		if !IsValidChannel(channels[i]) {
 | 
			
		||||
			return &ErrInvalidTarget{Target: channels[i]}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(buffer+","+channels[i]) > max {
 | 
			
		||||
			cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
 | 
			
		||||
			buffer = ""
 | 
			
		||||
@@ -53,91 +44,74 @@ func (cmd *Commands) Join(channels ...string) error {
 | 
			
		||||
 | 
			
		||||
		if i == len(channels)-1 {
 | 
			
		||||
			cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
 | 
			
		||||
			return nil
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinKey attempts to enter an IRC channel with a password.
 | 
			
		||||
func (cmd *Commands) JoinKey(channel, password string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) JoinKey(channel, password string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Part leaves an IRC channel.
 | 
			
		||||
func (cmd *Commands) Part(channel, message string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
func (cmd *Commands) Part(channels ...string) {
 | 
			
		||||
	for i := 0; i < len(channels); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: PART, Params: []string{channels[i]}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PartMessage leaves an IRC channel with a specified leave message.
 | 
			
		||||
func (cmd *Commands) PartMessage(channel, message string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) PartMessage(channel, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: PART, Params: []string{channel}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCP sends a CTCP request to target. Note that this method uses
 | 
			
		||||
// PRIVMSG specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCP(target, ctcpType, message string) error {
 | 
			
		||||
// PRIVMSG specifically. ctcpType is the CTCP command, e.g. "FINGER", "TIME",
 | 
			
		||||
// "VERSION", etc.
 | 
			
		||||
func (cmd *Commands) SendCTCP(target, ctcpType, message string) {
 | 
			
		||||
	out := encodeCTCPRaw(ctcpType, message)
 | 
			
		||||
	if out == "" {
 | 
			
		||||
		return errors.New("invalid CTCP")
 | 
			
		||||
		panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Message(target, out)
 | 
			
		||||
	cmd.Message(target, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCPf sends a CTCP request to target using a specific format. Note that
 | 
			
		||||
// this method uses PRIVMSG specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
// this method uses PRIVMSG specifically. ctcpType is the CTCP command, e.g.
 | 
			
		||||
// "FINGER", "TIME", "VERSION", etc.
 | 
			
		||||
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) {
 | 
			
		||||
	cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCPReplyf sends a CTCP response to target using a specific format.
 | 
			
		||||
// Note that this method uses NOTICE specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
// Note that this method uses NOTICE specifically. ctcpType is the CTCP
 | 
			
		||||
// command, e.g. "FINGER", "TIME", "VERSION", etc.
 | 
			
		||||
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) {
 | 
			
		||||
	cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendCTCPReply sends a CTCP response to target. Note that this method uses
 | 
			
		||||
// NOTICE specifically.
 | 
			
		||||
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) error {
 | 
			
		||||
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) {
 | 
			
		||||
	out := encodeCTCPRaw(ctcpType, message)
 | 
			
		||||
	if out == "" {
 | 
			
		||||
		return errors.New("invalid CTCP")
 | 
			
		||||
		panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Notice(target, out)
 | 
			
		||||
	cmd.Notice(target, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message sends a PRIVMSG to target (either channel, service, or user).
 | 
			
		||||
func (cmd *Commands) Message(target, message string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) Message(target, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messagef sends a formated PRIVMSG to target (either channel, service, or
 | 
			
		||||
// user).
 | 
			
		||||
func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Message(target, fmt.Sprintf(format, a...))
 | 
			
		||||
func (cmd *Commands) Messagef(target, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Message(target, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrInvalidSource is returned when a method needs to know the origin of an
 | 
			
		||||
@@ -146,94 +120,95 @@ func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
 | 
			
		||||
var ErrInvalidSource = errors.New("event has nil or invalid source address")
 | 
			
		||||
 | 
			
		||||
// Reply sends a reply to channel or user, based on where the supplied event
 | 
			
		||||
// originated from. See also ReplyTo().
 | 
			
		||||
func (cmd *Commands) Reply(event Event, message string) error {
 | 
			
		||||
// originated from. See also ReplyTo(). Panics if the incoming event has no
 | 
			
		||||
// source.
 | 
			
		||||
func (cmd *Commands) Reply(event Event, message string) {
 | 
			
		||||
	if event.Source == nil {
 | 
			
		||||
		return ErrInvalidSource
 | 
			
		||||
		panic(ErrInvalidSource)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
 | 
			
		||||
		return cmd.Message(event.Params[0], message)
 | 
			
		||||
		cmd.Message(event.Params[0], message)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Message(event.Source.Name, message)
 | 
			
		||||
	cmd.Message(event.Source.Name, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Replyf sends a reply to channel or user with a format string, based on
 | 
			
		||||
// where the supplied event originated from. See also ReplyTof().
 | 
			
		||||
func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Reply(event, fmt.Sprintf(format, a...))
 | 
			
		||||
// where the supplied event originated from. See also ReplyTof(). Panics if
 | 
			
		||||
// the incoming event has no source.
 | 
			
		||||
func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Reply(event, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReplyTo sends a reply to a channel or user, based on where the supplied
 | 
			
		||||
// event originated from. ReplyTo(), when originating from a channel will
 | 
			
		||||
// default to replying with "<user>, <message>". See also Reply().
 | 
			
		||||
func (cmd *Commands) ReplyTo(event Event, message string) error {
 | 
			
		||||
// default to replying with "<user>, <message>". See also Reply(). Panics if
 | 
			
		||||
// the incoming event has no source.
 | 
			
		||||
func (cmd *Commands) ReplyTo(event Event, message string) {
 | 
			
		||||
	if event.Source == nil {
 | 
			
		||||
		return ErrInvalidSource
 | 
			
		||||
		panic(ErrInvalidSource)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
 | 
			
		||||
		return cmd.Message(event.Params[0], event.Source.Name+", "+message)
 | 
			
		||||
		cmd.Message(event.Params[0], event.Source.Name+", "+message)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cmd.Message(event.Source.Name, message)
 | 
			
		||||
	cmd.Message(event.Source.Name, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReplyTof sends a reply to a channel or user with a format string, based
 | 
			
		||||
// on where the supplied event originated from. ReplyTo(), when originating
 | 
			
		||||
// from a channel will default to replying with "<user>, <message>". See
 | 
			
		||||
// also Replyf().
 | 
			
		||||
func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.ReplyTo(event, fmt.Sprintf(format, a...))
 | 
			
		||||
// also Replyf(). Panics if the incoming event has no source.
 | 
			
		||||
func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) {
 | 
			
		||||
	cmd.ReplyTo(event, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Action sends a PRIVMSG ACTION (/me) to target (either channel, service,
 | 
			
		||||
// or user).
 | 
			
		||||
func (cmd *Commands) Action(target, message string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) Action(target, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{
 | 
			
		||||
		Command:  PRIVMSG,
 | 
			
		||||
		Params:   []string{target},
 | 
			
		||||
		Trailing: fmt.Sprintf("\001ACTION %s\001", message),
 | 
			
		||||
	})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel,
 | 
			
		||||
// service, or user).
 | 
			
		||||
func (cmd *Commands) Actionf(target, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Action(target, fmt.Sprintf(format, a...))
 | 
			
		||||
func (cmd *Commands) Actionf(target, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Action(target, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Notice sends a NOTICE to target (either channel, service, or user).
 | 
			
		||||
func (cmd *Commands) Notice(target, message string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) Notice(target, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Noticef sends a formated NOTICE to target (either channel, service, or
 | 
			
		||||
// user).
 | 
			
		||||
func (cmd *Commands) Noticef(target, format string, a ...interface{}) error {
 | 
			
		||||
	return cmd.Notice(target, fmt.Sprintf(format, a...))
 | 
			
		||||
func (cmd *Commands) Noticef(target, format string, a ...interface{}) {
 | 
			
		||||
	cmd.Notice(target, fmt.Sprintf(format, a...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendRaw sends a raw string back to the server, without carriage returns
 | 
			
		||||
// or newlines.
 | 
			
		||||
func (cmd *Commands) SendRaw(raw string) error {
 | 
			
		||||
	e := ParseEvent(raw)
 | 
			
		||||
	if e == nil {
 | 
			
		||||
		return errors.New("invalid event: " + raw)
 | 
			
		||||
// SendRaw sends a raw string (or multiple) to the server, without carriage
 | 
			
		||||
// returns or newlines. Returns an error if one of the raw strings cannot be
 | 
			
		||||
// properly parsed.
 | 
			
		||||
func (cmd *Commands) SendRaw(raw ...string) error {
 | 
			
		||||
	var event *Event
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(raw); i++ {
 | 
			
		||||
		event = ParseEvent(raw[i])
 | 
			
		||||
		if event == nil {
 | 
			
		||||
			return errors.New("invalid event: " + raw[i])
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cmd.c.Send(event)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(e)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -246,31 +221,26 @@ func (cmd *Commands) SendRawf(format string, a ...interface{}) error {
 | 
			
		||||
// Topic sets the topic of channel to message. Does not verify the length
 | 
			
		||||
// of the topic.
 | 
			
		||||
func (cmd *Commands) Topic(channel, message string) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message})
 | 
			
		||||
	cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message, EmptyTrailing: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Who sends a WHO query to the server, which will attempt WHOX by default.
 | 
			
		||||
// See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This
 | 
			
		||||
// sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with
 | 
			
		||||
// girc's builtin tracking functionality.
 | 
			
		||||
func (cmd *Commands) Who(target string) error {
 | 
			
		||||
	if !IsValidNick(target) && !IsValidChannel(target) && !IsValidUser(target) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: target}
 | 
			
		||||
func (cmd *Commands) Who(users ...string) {
 | 
			
		||||
	for i := 0; i < len(users); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: WHO, Params: []string{users[i], "%tcuhnr,2"}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhnr,2"}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Whois sends a WHOIS query to the server, targeted at a specific user.
 | 
			
		||||
// as WHOIS is a bit slower, you may want to use WHO for brief user info.
 | 
			
		||||
func (cmd *Commands) Whois(nick string) error {
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
// Whois sends a WHOIS query to the server, targeted at a specific user (or
 | 
			
		||||
// set of users). As WHOIS is a bit slower, you may want to use WHO for brief
 | 
			
		||||
// user info.
 | 
			
		||||
func (cmd *Commands) Whois(users ...string) {
 | 
			
		||||
	for i := 0; i < len(users); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: WHOIS, Params: []string{users[i]}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHOIS, Params: []string{nick}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ping sends a PING query to the server, with a specific identifier that
 | 
			
		||||
@@ -294,36 +264,40 @@ func (cmd *Commands) Oper(user, pass string) {
 | 
			
		||||
// Kick sends a KICK query to the server, attempting to kick nick from
 | 
			
		||||
// channel, with reason. If reason is blank, one will not be sent to the
 | 
			
		||||
// server.
 | 
			
		||||
func (cmd *Commands) Kick(channel, nick, reason string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func (cmd *Commands) Kick(channel, user, reason string) {
 | 
			
		||||
	if reason != "" {
 | 
			
		||||
		cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}, Trailing: reason})
 | 
			
		||||
		return nil
 | 
			
		||||
		cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}, Trailing: reason, EmptyTrailing: true})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}})
 | 
			
		||||
	return nil
 | 
			
		||||
	cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ban adds the +b mode on the given mask on a channel.
 | 
			
		||||
func (cmd *Commands) Ban(channel, mask string) {
 | 
			
		||||
	cmd.Mode(channel, "+b", mask)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unban removes the +b mode on the given mask on a channel.
 | 
			
		||||
func (cmd *Commands) Unban(channel, mask string) {
 | 
			
		||||
	cmd.Mode(channel, "-b", mask)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mode sends a mode change to the server which should be applied to target
 | 
			
		||||
// (usually a channel or user), along with a set of modes (generally "+m",
 | 
			
		||||
// "+mmmm", or "-m", where "m" is the mode you want to change). Params is only
 | 
			
		||||
// needed if the mode change requires a parameter (ban or invite-only exclude.)
 | 
			
		||||
func (cmd *Commands) Mode(target, modes string, params ...string) {
 | 
			
		||||
	out := []string{target, modes}
 | 
			
		||||
	out = append(out, params...)
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: MODE, Params: out})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Invite sends a INVITE query to the server, to invite nick to channel.
 | 
			
		||||
func (cmd *Commands) Invite(channel, nick string) error {
 | 
			
		||||
	if !IsValidChannel(channel) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: channel}
 | 
			
		||||
func (cmd *Commands) Invite(channel string, users ...string) {
 | 
			
		||||
	for i := 0; i < len(users); i++ {
 | 
			
		||||
		cmd.c.Send(&Event{Command: INVITE, Params: []string{users[i], channel}})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: INVITE, Params: []string{nick, channel}})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Away sends a AWAY query to the server, suggesting that the client is no
 | 
			
		||||
@@ -348,10 +322,10 @@ func (cmd *Commands) Back() {
 | 
			
		||||
// Supports multiple channels at once, in hopes it will reduce extensive
 | 
			
		||||
// LIST queries to the server. Supply no channels to run a list against the
 | 
			
		||||
// entire server (warning, that may mean LOTS of channels!)
 | 
			
		||||
func (cmd *Commands) List(channels ...string) error {
 | 
			
		||||
func (cmd *Commands) List(channels ...string) {
 | 
			
		||||
	if len(channels) == 0 {
 | 
			
		||||
		cmd.c.Send(&Event{Command: LIST})
 | 
			
		||||
		return nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We can LIST multiple channels at once, however we need to ensure that
 | 
			
		||||
@@ -361,10 +335,6 @@ func (cmd *Commands) List(channels ...string) error {
 | 
			
		||||
	var buffer string
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(channels); i++ {
 | 
			
		||||
		if !IsValidChannel(channels[i]) {
 | 
			
		||||
			return &ErrInvalidTarget{Target: channels[i]}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(buffer+","+channels[i]) > max {
 | 
			
		||||
			cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
 | 
			
		||||
			buffer = ""
 | 
			
		||||
@@ -379,20 +349,13 @@ func (cmd *Commands) List(channels ...string) error {
 | 
			
		||||
 | 
			
		||||
		if i == len(channels)-1 {
 | 
			
		||||
			cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
 | 
			
		||||
			return nil
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Whowas sends a WHOWAS query to the server. amount is the amount of results
 | 
			
		||||
// you want back.
 | 
			
		||||
func (cmd *Commands) Whowas(nick string, amount int) error {
 | 
			
		||||
	if !IsValidNick(nick) {
 | 
			
		||||
		return &ErrInvalidTarget{Target: nick}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHOWAS, Params: []string{nick, string(amount)}})
 | 
			
		||||
	return nil
 | 
			
		||||
func (cmd *Commands) Whowas(user string, amount int) {
 | 
			
		||||
	cmd.c.Send(&Event{Command: WHOWAS, Params: []string{user, string(amount)}})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/lrstanley/girc/ctcp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/lrstanley/girc/ctcp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -58,7 +58,7 @@ func decodeCTCP(e *Event) *CTCPEvent {
 | 
			
		||||
	if s < 0 {
 | 
			
		||||
		for i := 0; i < len(text); i++ {
 | 
			
		||||
			// Check for A-Z, 0-9.
 | 
			
		||||
			if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
 | 
			
		||||
			if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -74,7 +74,7 @@ func decodeCTCP(e *Event) *CTCPEvent {
 | 
			
		||||
	// Loop through checking the tag first.
 | 
			
		||||
	for i := 0; i < s; i++ {
 | 
			
		||||
		// Check for A-Z, 0-9.
 | 
			
		||||
		if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
 | 
			
		||||
		if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -168,7 +168,7 @@ func (c *CTCP) parseCMD(cmd string) string {
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(cmd); i++ {
 | 
			
		||||
		// Check for A-Z, 0-9.
 | 
			
		||||
		if (cmd[i] < 0x41 || cmd[i] > 0x5A) && (cmd[i] < 0x30 || cmd[i] > 0x39) {
 | 
			
		||||
		if (cmd[i] < 'A' || cmd[i] > 'Z') && (cmd[i] < '0' || cmd[i] > '9') {
 | 
			
		||||
			return ""
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/lrstanley/girc/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/lrstanley/girc/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,8 +11,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	eventSpace byte = 0x20 // Separator.
 | 
			
		||||
	maxLength       = 510  // Maximum length is 510 (2 for line endings).
 | 
			
		||||
	eventSpace byte = ' ' // Separator.
 | 
			
		||||
	maxLength       = 510 // Maximum length is 510 (2 for line endings).
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// cutCRFunc is used to trim CR characters from prefixes/messages.
 | 
			
		||||
@@ -256,7 +256,7 @@ func (e *Event) Bytes() []byte {
 | 
			
		||||
 | 
			
		||||
	// Strip newlines and carriage returns.
 | 
			
		||||
	for i := 0; i < len(out); i++ {
 | 
			
		||||
		if out[i] == 0x0A || out[i] == 0x0D {
 | 
			
		||||
		if out[i] == '\n' || out[i] == '\r' {
 | 
			
		||||
			out = append(out[:i], out[i+1:]...)
 | 
			
		||||
			i-- // Decrease the index so we can pick up where we left off.
 | 
			
		||||
		}
 | 
			
		||||
@@ -432,9 +432,9 @@ func (e *Event) StripAction() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	messagePrefix byte = 0x3A // ":" -- prefix or last argument
 | 
			
		||||
	prefixIdent   byte = 0x21 // "!" -- username
 | 
			
		||||
	prefixHost    byte = 0x40 // "@" -- hostname
 | 
			
		||||
	messagePrefix byte = ':' // Prefix or last argument.
 | 
			
		||||
	prefixIdent   byte = '!' // Username.
 | 
			
		||||
	prefixHost    byte = '@' // Hostname.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Source represents the sender of an IRC event, see RFC1459 section 2.3.1.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/lrstanley/girc/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/lrstanley/girc/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,8 +12,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	fmtOpenChar  = 0x7B // {
 | 
			
		||||
	fmtCloseChar = 0x7D // }
 | 
			
		||||
	fmtOpenChar  = '{'
 | 
			
		||||
	fmtCloseChar = '}'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var fmtColors = map[string]int{
 | 
			
		||||
@@ -113,7 +113,7 @@ func Fmt(text string) string {
 | 
			
		||||
 | 
			
		||||
		if last > -1 {
 | 
			
		||||
			// A-Z, a-z, and ","
 | 
			
		||||
			if text[i] != 0x2c && (text[i] <= 0x41 || text[i] >= 0x5a) && (text[i] <= 0x61 || text[i] >= 0x7a) {
 | 
			
		||||
			if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') {
 | 
			
		||||
				last = -1
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
@@ -127,10 +127,10 @@ func Fmt(text string) string {
 | 
			
		||||
// See Fmt() for more information.
 | 
			
		||||
func TrimFmt(text string) string {
 | 
			
		||||
	for color := range fmtColors {
 | 
			
		||||
		text = strings.Replace(text, "{"+color+"}", "", -1)
 | 
			
		||||
		text = strings.Replace(text, string(fmtOpenChar)+color+string(fmtCloseChar), "", -1)
 | 
			
		||||
	}
 | 
			
		||||
	for code := range fmtCodes {
 | 
			
		||||
		text = strings.Replace(text, "{"+code+"}", "", -1)
 | 
			
		||||
		text = strings.Replace(text, string(fmtOpenChar)+code+string(fmtCloseChar), "", -1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return text
 | 
			
		||||
@@ -175,9 +175,10 @@ func IsValidChannel(channel string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// #, +, !<channelid>, or &
 | 
			
		||||
	// Including "*" in the prefix list, as this is commonly used (e.g. ZNC)
 | 
			
		||||
	if bytes.IndexByte([]byte{0x21, 0x23, 0x26, 0x2A, 0x2B}, channel[0]) == -1 {
 | 
			
		||||
	// #, +, !<channelid>, ~, or &
 | 
			
		||||
	// Including "*" and "~" in the prefix list, as these are commonly used
 | 
			
		||||
	// (e.g. ZNC.)
 | 
			
		||||
	if bytes.IndexByte([]byte{'!', '#', '&', '*', '~', '+'}, channel[0]) == -1 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -186,14 +187,14 @@ func IsValidChannel(channel string) bool {
 | 
			
		||||
	//   1 (prefix) + 5 (id) + 1 (+, channel name)
 | 
			
		||||
	// On some networks, this may be extended with ISUPPORT capabilities,
 | 
			
		||||
	// however this is extremely uncommon.
 | 
			
		||||
	if channel[0] == 0x21 {
 | 
			
		||||
	if channel[0] == '!' {
 | 
			
		||||
		if len(channel) < 7 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check for valid ID
 | 
			
		||||
		for i := 1; i < 6; i++ {
 | 
			
		||||
			if (channel[i] < 0x30 || channel[i] > 0x39) && (channel[i] < 0x41 || channel[i] > 0x5A) {
 | 
			
		||||
			if (channel[i] < '0' || channel[i] > '9') && (channel[i] < 'A' || channel[i] > 'Z') {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -222,17 +223,15 @@ func IsValidNick(nick string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nick = ToRFC1459(nick)
 | 
			
		||||
 | 
			
		||||
	// Check the first index. Some characters aren't allowed for the first
 | 
			
		||||
	// index of an IRC nickname.
 | 
			
		||||
	if nick[0] < 0x41 || nick[0] > 0x7D {
 | 
			
		||||
		// a-z, A-Z, and _\[]{}^|
 | 
			
		||||
	if (nick[0] < 'A' || nick[0] > '}') && nick[0] != '?' {
 | 
			
		||||
		// a-z, A-Z, '_\[]{}^|', and '?' in the case of znc.
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 1; i < len(nick); i++ {
 | 
			
		||||
		if (nick[i] < 0x41 || nick[i] > 0x7D) && (nick[i] < 0x30 || nick[i] > 0x39) && nick[i] != 0x2D {
 | 
			
		||||
		if (nick[i] < 'A' || nick[i] > '}') && (nick[i] < '0' || nick[i] > '9') && nick[i] != '-' {
 | 
			
		||||
			// a-z, A-Z, 0-9, -, and _\[]{}^|
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
@@ -261,10 +260,8 @@ func IsValidUser(name string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name = ToRFC1459(name)
 | 
			
		||||
 | 
			
		||||
	// "~" is prepended (commonly) if there was no ident server response.
 | 
			
		||||
	if name[0] == 0x7E {
 | 
			
		||||
	if name[0] == '~' {
 | 
			
		||||
		// Means name only contained "~".
 | 
			
		||||
		if len(name) < 2 {
 | 
			
		||||
			return false
 | 
			
		||||
@@ -274,12 +271,12 @@ func IsValidUser(name string) bool {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check to see if the first index is alphanumeric.
 | 
			
		||||
	if (name[0] < 0x41 || name[0] > 0x4A) && (name[0] < 0x61 || name[0] > 0x7A) && (name[0] < 0x30 || name[0] > 0x39) {
 | 
			
		||||
	if (name[0] < 'A' || name[0] > 'J') && (name[0] < 'a' || name[0] > 'z') && (name[0] < '0' || name[0] > '9') {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 1; i < len(name); i++ {
 | 
			
		||||
		if (name[i] < 0x41 || name[i] > 0x7D) && (name[i] < 0x30 || name[i] > 0x39) && name[i] != 0x2D && name[i] != 0x2E {
 | 
			
		||||
		if (name[i] < 'A' || name[i] > '}') && (name[i] < '0' || name[i] > '9') && name[i] != '-' && name[i] != '.' {
 | 
			
		||||
			// a-z, A-Z, 0-9, -, and _\[]{}^|
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
@@ -290,8 +287,13 @@ func IsValidUser(name string) bool {
 | 
			
		||||
 | 
			
		||||
// ToRFC1459 converts a string to the stripped down conversion within RFC
 | 
			
		||||
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
 | 
			
		||||
// and so forth. Useful to compare two nicknames or channels.
 | 
			
		||||
func ToRFC1459(input string) (out string) {
 | 
			
		||||
// and so forth. Useful to compare two nicknames or channels. Note that this
 | 
			
		||||
// should not be used to normalize nicknames or similar, as this may convert
 | 
			
		||||
// valid input characters to non-rfc-valid characters. As such, it's main use
 | 
			
		||||
// is for comparing two nicks.
 | 
			
		||||
func ToRFC1459(input string) string {
 | 
			
		||||
	var out string
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(input); i++ {
 | 
			
		||||
		if input[i] >= 65 && input[i] <= 94 {
 | 
			
		||||
			out += string(rune(input[i]) + 32)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								vendor/github.com/lrstanley/girc/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										131
									
								
								vendor/github.com/lrstanley/girc/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -29,11 +29,12 @@ func (c *Client) RunHandlers(event *Event) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Regular wildcard handlers.
 | 
			
		||||
	c.Handlers.exec(ALL_EVENTS, c, event.Copy())
 | 
			
		||||
	// Background handlers first.
 | 
			
		||||
	c.Handlers.exec(ALL_EVENTS, true, c, event.Copy())
 | 
			
		||||
	c.Handlers.exec(event.Command, true, c, event.Copy())
 | 
			
		||||
 | 
			
		||||
	// Then regular handlers.
 | 
			
		||||
	c.Handlers.exec(event.Command, c, event.Copy())
 | 
			
		||||
	c.Handlers.exec(ALL_EVENTS, false, c, event.Copy())
 | 
			
		||||
	c.Handlers.exec(event.Command, false, c, event.Copy())
 | 
			
		||||
 | 
			
		||||
	// Check if it's a CTCP.
 | 
			
		||||
	if ctcp := decodeCTCP(event.Copy()); ctcp != nil {
 | 
			
		||||
@@ -144,7 +145,7 @@ func (c *Caller) cuid(cmd string, n int) (cuid, uid string) {
 | 
			
		||||
// cuidToID allows easy mapping between a generated cuid and the caller
 | 
			
		||||
// external/internal handler maps.
 | 
			
		||||
func (c *Caller) cuidToID(input string) (cmd, uid string) {
 | 
			
		||||
	i := strings.IndexByte(input, 0x3A)
 | 
			
		||||
	i := strings.IndexByte(input, ':')
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		return "", ""
 | 
			
		||||
	}
 | 
			
		||||
@@ -160,9 +161,9 @@ type execStack struct {
 | 
			
		||||
// exec executes all handlers pertaining to specified event. Internal first,
 | 
			
		||||
// then external.
 | 
			
		||||
//
 | 
			
		||||
// Please note that there is no specific order/priority for which the
 | 
			
		||||
// handler types themselves or the handlers are executed.
 | 
			
		||||
func (c *Caller) exec(command string, client *Client, event *Event) {
 | 
			
		||||
// Please note that there is no specific order/priority for which the handlers
 | 
			
		||||
// are executed.
 | 
			
		||||
func (c *Caller) exec(command string, bg bool, client *Client, event *Event) {
 | 
			
		||||
	// Build a stack of handlers which can be executed concurrently.
 | 
			
		||||
	var stack []execStack
 | 
			
		||||
 | 
			
		||||
@@ -170,13 +171,21 @@ func (c *Caller) exec(command string, client *Client, event *Event) {
 | 
			
		||||
	// Get internal handlers first.
 | 
			
		||||
	if _, ok := c.internal[command]; ok {
 | 
			
		||||
		for cuid := range c.internal[command] {
 | 
			
		||||
			if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stack = append(stack, execStack{c.internal[command][cuid], cuid})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Aaand then external handlers.
 | 
			
		||||
	// Then external handlers.
 | 
			
		||||
	if _, ok := c.external[command]; ok {
 | 
			
		||||
		for cuid := range c.external[command] {
 | 
			
		||||
			if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stack = append(stack, execStack{c.external[command][cuid], cuid})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -189,18 +198,29 @@ func (c *Caller) exec(command string, client *Client, event *Event) {
 | 
			
		||||
	wg.Add(len(stack))
 | 
			
		||||
	for i := 0; i < len(stack); i++ {
 | 
			
		||||
		go func(index int) {
 | 
			
		||||
			c.debug.Printf("executing handler %s for event %s (%d of %d)", stack[index].cuid, command, index+1, len(stack))
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			c.debug.Printf("[%d/%d] exec %s => %s", index+1, len(stack), stack[index].cuid, command)
 | 
			
		||||
			start := time.Now()
 | 
			
		||||
 | 
			
		||||
			// If they want to catch any panics, add to defer stack.
 | 
			
		||||
			if bg {
 | 
			
		||||
				go func() {
 | 
			
		||||
					if client.Config.RecoverFunc != nil {
 | 
			
		||||
						defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					stack[index].Execute(client, *event)
 | 
			
		||||
					c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start))
 | 
			
		||||
				}()
 | 
			
		||||
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if client.Config.RecoverFunc != nil {
 | 
			
		||||
				defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stack[index].Execute(client, *event)
 | 
			
		||||
 | 
			
		||||
			c.debug.Printf("execution of %s took %s (%d of %d)", stack[index].cuid, time.Since(start), index+1, len(stack))
 | 
			
		||||
			wg.Done()
 | 
			
		||||
			c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start))
 | 
			
		||||
		}(i)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -281,9 +301,9 @@ func (c *Caller) remove(cuid string) (success bool) {
 | 
			
		||||
 | 
			
		||||
// sregister is much like Caller.register(), except that it safely locks
 | 
			
		||||
// the Caller mutex.
 | 
			
		||||
func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
func (c *Caller) sregister(internal, bg bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	cuid = c.register(internal, cmd, handler)
 | 
			
		||||
	cuid = c.register(internal, bg, cmd, handler)
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	return cuid
 | 
			
		||||
@@ -291,30 +311,34 @@ func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid str
 | 
			
		||||
 | 
			
		||||
// register will register a handler in the internal tracker. Unsafe (you
 | 
			
		||||
// must lock c.mu yourself!)
 | 
			
		||||
func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
func (c *Caller) register(internal, bg bool, cmd string, handler Handler) (cuid string) {
 | 
			
		||||
	var uid string
 | 
			
		||||
 | 
			
		||||
	cmd = strings.ToUpper(cmd)
 | 
			
		||||
 | 
			
		||||
	cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
	if bg {
 | 
			
		||||
		uid += ":bg"
 | 
			
		||||
		cuid += ":bg"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if internal {
 | 
			
		||||
		if _, ok := c.internal[cmd]; !ok {
 | 
			
		||||
			c.internal[cmd] = map[string]Handler{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
		c.internal[cmd][uid] = handler
 | 
			
		||||
	} else {
 | 
			
		||||
		if _, ok := c.external[cmd]; !ok {
 | 
			
		||||
			c.external[cmd] = map[string]Handler{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
		c.external[cmd][uid] = handler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, file, line, _ := runtime.Caller(3)
 | 
			
		||||
 | 
			
		||||
	c.debug.Printf("registering handler for %q with cuid %q (internal: %t) from: %s:%d", cmd, cuid, internal, file, line)
 | 
			
		||||
	c.debug.Printf("reg %q => %s [int:%t bg:%t] %s:%d", uid, cmd, internal, bg, file, line)
 | 
			
		||||
 | 
			
		||||
	return cuid
 | 
			
		||||
}
 | 
			
		||||
@@ -323,31 +347,20 @@ func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid stri
 | 
			
		||||
// given event. cuid is the handler uid which can be used to remove the
 | 
			
		||||
// handler with Caller.Remove().
 | 
			
		||||
func (c *Caller) AddHandler(cmd string, handler Handler) (cuid string) {
 | 
			
		||||
	return c.sregister(false, cmd, handler)
 | 
			
		||||
	return c.sregister(false, false, cmd, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add registers the handler function for the given event. cuid is the
 | 
			
		||||
// handler uid which can be used to remove the handler with Caller.Remove().
 | 
			
		||||
func (c *Caller) Add(cmd string, handler func(client *Client, event Event)) (cuid string) {
 | 
			
		||||
	return c.sregister(false, cmd, HandlerFunc(handler))
 | 
			
		||||
	return c.sregister(false, false, cmd, HandlerFunc(handler))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddBg registers the handler function for the given event and executes it
 | 
			
		||||
// in a go-routine. cuid is the handler uid which can be used to remove the
 | 
			
		||||
// handler with Caller.Remove().
 | 
			
		||||
func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (cuid string) {
 | 
			
		||||
	return c.sregister(false, cmd, HandlerFunc(func(client *Client, event Event) {
 | 
			
		||||
		// Setting up background-based handlers this way allows us to get
 | 
			
		||||
		// clean call stacks for use with panic recovery.
 | 
			
		||||
		go func() {
 | 
			
		||||
			// If they want to catch any panics, add to defer stack.
 | 
			
		||||
			if client.Config.RecoverFunc != nil {
 | 
			
		||||
				defer recoverHandlerPanic(client, &event, "goroutine", 3)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			handler(client, event)
 | 
			
		||||
		}()
 | 
			
		||||
	}))
 | 
			
		||||
	return c.sregister(false, true, cmd, HandlerFunc(handler))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddTmp adds a "temporary" handler, which is good for one-time or few-time
 | 
			
		||||
@@ -361,47 +374,37 @@ func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (c
 | 
			
		||||
//
 | 
			
		||||
// Additionally, AddTmp has a useful option, deadline. When set to greater
 | 
			
		||||
// than 0, deadline will be the amount of time that passes before the handler
 | 
			
		||||
// is removed from the stack, regardless if the handler returns true or not.
 | 
			
		||||
// is removed from the stack, regardless of if the handler returns true or not.
 | 
			
		||||
// This is useful in that it ensures that the handler is cleaned up if the
 | 
			
		||||
// server does not respond appropriately, or takes too long to respond.
 | 
			
		||||
//
 | 
			
		||||
// Note that handlers supplied with AddTmp are executed in a goroutine to
 | 
			
		||||
// ensure that they are not blocking other handlers. Additionally, use cuid
 | 
			
		||||
// with Caller.Remove() to prematurely remove the handler from the stack,
 | 
			
		||||
// bypassing the timeout or waiting for the handler to return that it wants
 | 
			
		||||
// to be removed from the stack.
 | 
			
		||||
// ensure that they are not blocking other handlers. However, if you are
 | 
			
		||||
// creating a temporary handler from another handler, it should be a
 | 
			
		||||
// background handler.
 | 
			
		||||
//
 | 
			
		||||
// Use cuid with Caller.Remove() to prematurely remove the handler from the
 | 
			
		||||
// stack, bypassing the timeout or waiting for the handler to return that it
 | 
			
		||||
// wants to be removed from the stack.
 | 
			
		||||
func (c *Caller) AddTmp(cmd string, deadline time.Duration, handler func(client *Client, event Event) bool) (cuid string, done chan struct{}) {
 | 
			
		||||
	var uid string
 | 
			
		||||
	cuid, uid = c.cuid(cmd, 20)
 | 
			
		||||
 | 
			
		||||
	done = make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	c.mu.Lock()
 | 
			
		||||
	if _, ok := c.external[cmd]; !ok {
 | 
			
		||||
		c.external[cmd] = map[string]Handler{}
 | 
			
		||||
	}
 | 
			
		||||
	c.external[cmd][uid] = HandlerFunc(func(client *Client, event Event) {
 | 
			
		||||
		// Setting up background-based handlers this way allows us to get
 | 
			
		||||
		// clean call stacks for use with panic recovery.
 | 
			
		||||
		go func() {
 | 
			
		||||
			// If they want to catch any panics, add to defer stack.
 | 
			
		||||
			if client.Config.RecoverFunc != nil {
 | 
			
		||||
				defer recoverHandlerPanic(client, &event, "tmp-goroutine", 3)
 | 
			
		||||
	cuid = c.sregister(false, true, cmd, HandlerFunc(func(client *Client, event Event) {
 | 
			
		||||
		remove := handler(client, event)
 | 
			
		||||
		if remove {
 | 
			
		||||
			if ok := c.Remove(cuid); ok {
 | 
			
		||||
				close(done)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			remove := handler(client, event)
 | 
			
		||||
			if remove {
 | 
			
		||||
				if ok := c.Remove(cuid); ok {
 | 
			
		||||
					close(done)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
	})
 | 
			
		||||
	c.mu.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
 | 
			
		||||
	if deadline > 0 {
 | 
			
		||||
		go func() {
 | 
			
		||||
			<-time.After(deadline)
 | 
			
		||||
			select {
 | 
			
		||||
			case <-time.After(deadline):
 | 
			
		||||
			case <-done:
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ok := c.Remove(cuid); ok {
 | 
			
		||||
				close(done)
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/lrstanley/girc/modes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/lrstanley/girc/modes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -206,11 +206,11 @@ func (c *CModes) Parse(flags string, args []string) (out []CMode) {
 | 
			
		||||
	var argCount int
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(flags); i++ {
 | 
			
		||||
		if flags[i] == 0x2B {
 | 
			
		||||
		if flags[i] == '+' {
 | 
			
		||||
			add = true
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if flags[i] == 0x2D {
 | 
			
		||||
		if flags[i] == '-' {
 | 
			
		||||
			add = false
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -265,7 +265,7 @@ func IsValidChannelMode(raw string) bool {
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(raw); i++ {
 | 
			
		||||
		// Allowed are: ",", A-Z and a-z.
 | 
			
		||||
		if raw[i] != 0x2C && (raw[i] < 0x41 || raw[i] > 0x5A) && (raw[i] < 0x61 || raw[i] > 0x7A) {
 | 
			
		||||
		if raw[i] != ',' && (raw[i] < 'A' || raw[i] > 'Z') && (raw[i] < 'a' || raw[i] > 'z') {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -279,7 +279,7 @@ func isValidUserPrefix(raw string) bool {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if raw[0] != 0x28 { // (.
 | 
			
		||||
	if raw[0] != '(' {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -288,7 +288,7 @@ func isValidUserPrefix(raw string) bool {
 | 
			
		||||
 | 
			
		||||
	// Skip the first one as we know it's (.
 | 
			
		||||
	for i := 1; i < len(raw); i++ {
 | 
			
		||||
		if raw[i] == 0x29 { // ).
 | 
			
		||||
		if raw[i] == ')' {
 | 
			
		||||
			passedKeys = true
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								vendor/github.com/matrix-org/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										90
									
								
								vendor/github.com/matrix-org/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -79,7 +79,7 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string {
 | 
			
		||||
	return hsURL.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildURLWithQuery builds a URL with query paramters in addition to the Client's homeserver/prefix/access_token set already.
 | 
			
		||||
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already.
 | 
			
		||||
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
 | 
			
		||||
	u, _ := url.Parse(cli.BuildURL(urlPath...))
 | 
			
		||||
	q := u.Query()
 | 
			
		||||
@@ -387,6 +387,20 @@ func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", mxid, "displayname")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) SetDisplayName(displayName string) (err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
 | 
			
		||||
@@ -450,6 +464,35 @@ func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
		TextMessage{"m.text", text})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendImage sends an m.room.message event into the given room with a msgtype of m.image
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
 | 
			
		||||
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		ImageMessage{
 | 
			
		||||
			MsgType: "m.image",
 | 
			
		||||
			Body:    body,
 | 
			
		||||
			URL:     url,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		VideoMessage{
 | 
			
		||||
			MsgType: "m.video",
 | 
			
		||||
			Body:    body,
 | 
			
		||||
			URL:     url,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
 | 
			
		||||
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		TextMessage{"m.notice", text})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
 | 
			
		||||
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
 | 
			
		||||
	txnID := txnID()
 | 
			
		||||
@@ -518,6 +561,14 @@ func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanU
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
 | 
			
		||||
	req := ReqTyping{Typing: typing, Timeout: timeout}
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
 | 
			
		||||
// the HTTP response body, or return an error.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
 | 
			
		||||
@@ -556,8 +607,15 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if res.StatusCode != 200 {
 | 
			
		||||
		contents, err := ioutil.ReadAll(res.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, HTTPError{
 | 
			
		||||
				Message: "Upload request failed - Failed to read response body: " + err.Error(),
 | 
			
		||||
				Code:    res.StatusCode,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil, HTTPError{
 | 
			
		||||
			Message: "Upload request failed",
 | 
			
		||||
			Message: "Upload request failed: " + string(contents),
 | 
			
		||||
			Code:    res.StatusCode,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -588,6 +646,34 @@ func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messages returns a list of message and state events for a room. It uses
 | 
			
		||||
// pagination query parameters to paginate history in the room.
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
 | 
			
		||||
func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"from": from,
 | 
			
		||||
		"dir":  string(dir),
 | 
			
		||||
	}
 | 
			
		||||
	if to != "" {
 | 
			
		||||
		query["to"] = to
 | 
			
		||||
	}
 | 
			
		||||
	if limit != 0 {
 | 
			
		||||
		query["limit"] = strconv.Itoa(limit)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TurnServer returns turn server details and credentials for the client to use when initiating calls.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
 | 
			
		||||
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("voip", "turnServer")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func txnID() string {
 | 
			
		||||
	return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,13 +7,13 @@ import (
 | 
			
		||||
 | 
			
		||||
// Event represents a single Matrix event.
 | 
			
		||||
type Event struct {
 | 
			
		||||
	StateKey  string                 `json:"state_key"`        // The state key for the event. Only present on State Events.
 | 
			
		||||
	Sender    string                 `json:"sender"`           // The user ID of the sender of the event
 | 
			
		||||
	Type      string                 `json:"type"`             // The event type
 | 
			
		||||
	Timestamp int                    `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
 | 
			
		||||
	ID        string                 `json:"event_id"`         // The unique ID of this event
 | 
			
		||||
	RoomID    string                 `json:"room_id"`          // The room the event was sent to. May be nil (e.g. for presence)
 | 
			
		||||
	Content   map[string]interface{} `json:"content"`          // The JSON content of the event.
 | 
			
		||||
	StateKey  *string                `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
 | 
			
		||||
	Sender    string                 `json:"sender"`              // The user ID of the sender of the event
 | 
			
		||||
	Type      string                 `json:"type"`                // The event type
 | 
			
		||||
	Timestamp int64                  `json:"origin_server_ts"`    // The unix timestamp when this message was sent by the origin server
 | 
			
		||||
	ID        string                 `json:"event_id"`            // The unique ID of this event
 | 
			
		||||
	RoomID    string                 `json:"room_id"`             // The room the event was sent to. May be nil (e.g. for presence)
 | 
			
		||||
	Content   map[string]interface{} `json:"content"`             // The JSON content of the event.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Body returns the value of the "body" key in the event content if it is
 | 
			
		||||
@@ -44,12 +44,31 @@ type TextMessage struct {
 | 
			
		||||
	Body    string `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageInfo contains info about an image
 | 
			
		||||
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
 | 
			
		||||
type ImageInfo struct {
 | 
			
		||||
	Height   uint   `json:"h"`
 | 
			
		||||
	Width    uint   `json:"w"`
 | 
			
		||||
	Mimetype string `json:"mimetype"`
 | 
			
		||||
	Size     uint   `json:"size"`
 | 
			
		||||
	Height   uint   `json:"h,omitempty"`
 | 
			
		||||
	Width    uint   `json:"w,omitempty"`
 | 
			
		||||
	Mimetype string `json:"mimetype,omitempty"`
 | 
			
		||||
	Size     uint   `json:"size,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
type VideoInfo struct {
 | 
			
		||||
	Mimetype      string    `json:"mimetype,omitempty"`
 | 
			
		||||
	ThumbnailInfo ImageInfo `json:"thumbnail_info"`
 | 
			
		||||
	ThumbnailURL  string    `json:"thumbnail_url,omitempty"`
 | 
			
		||||
	Height        uint      `json:"h,omitempty"`
 | 
			
		||||
	Width         uint      `json:"w,omitempty"`
 | 
			
		||||
	Duration      uint      `json:"duration,omitempty"`
 | 
			
		||||
	Size          uint      `json:"size,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VideoMessage is an m.video  - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
type VideoMessage struct {
 | 
			
		||||
	MsgType string    `json:"msgtype"`
 | 
			
		||||
	Body    string    `json:"body"`
 | 
			
		||||
	URL     string    `json:"url"`
 | 
			
		||||
	Info    VideoInfo `json:"info"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageMessage is an m.image event
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matrix-org/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
// Copyright 2017 Jan Christian Grünhage
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
 | 
			
		||||
//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
 | 
			
		||||
type Filter struct {
 | 
			
		||||
	AccountData FilterPart `json:"account_data,omitempty"`
 | 
			
		||||
	EventFields []string   `json:"event_fields,omitempty"`
 | 
			
		||||
	EventFormat string     `json:"event_format,omitempty"`
 | 
			
		||||
	Presence    FilterPart `json:"presence,omitempty"`
 | 
			
		||||
	Room        struct {
 | 
			
		||||
		AccountData  FilterPart `json:"account_data,omitempty"`
 | 
			
		||||
		Ephemeral    FilterPart `json:"ephemeral,omitempty"`
 | 
			
		||||
		IncludeLeave bool       `json:"include_leave,omitempty"`
 | 
			
		||||
		NotRooms     []string   `json:"not_rooms,omitempty"`
 | 
			
		||||
		Rooms        []string   `json:"rooms,omitempty"`
 | 
			
		||||
		State        FilterPart `json:"state,omitempty"`
 | 
			
		||||
		Timeline     FilterPart `json:"timeline,omitempty"`
 | 
			
		||||
	} `json:"room,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FilterPart struct {
 | 
			
		||||
	NotRooms   []string `json:"not_rooms,omitempty"`
 | 
			
		||||
	Rooms      []string `json:"rooms,omitempty"`
 | 
			
		||||
	Limit      *int     `json:"limit,omitempty"`
 | 
			
		||||
	NotSenders []string `json:"not_senders,omitempty"`
 | 
			
		||||
	NotTypes   []string `json:"not_types,omitempty"`
 | 
			
		||||
	Senders    []string `json:"senders,omitempty"`
 | 
			
		||||
	Types      []string `json:"types,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/matrix-org/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/matrix-org/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -70,3 +70,9 @@ type ReqBanUser struct {
 | 
			
		||||
type ReqUnbanUser struct {
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
type ReqTyping struct {
 | 
			
		||||
	Typing  bool  `json:"typing"`
 | 
			
		||||
	Timeout int64 `json:"timeout"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/matrix-org/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/matrix-org/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -45,6 +45,9 @@ type RespBanUser struct{}
 | 
			
		||||
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
type RespUnbanUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
type RespTyping struct{}
 | 
			
		||||
 | 
			
		||||
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
type RespJoinedRooms struct {
 | 
			
		||||
	JoinedRooms []string `json:"joined_rooms"`
 | 
			
		||||
@@ -58,6 +61,13 @@ type RespJoinedMembers struct {
 | 
			
		||||
	} `json:"joined"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
 | 
			
		||||
type RespMessages struct {
 | 
			
		||||
	Start string  `json:"start"`
 | 
			
		||||
	Chunk []Event `json:"chunk"`
 | 
			
		||||
	End   string  `json:"end"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
 | 
			
		||||
type RespSendEvent struct {
 | 
			
		||||
	EventID string `json:"event_id"`
 | 
			
		||||
@@ -90,6 +100,11 @@ func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
type RespUserDisplayName struct {
 | 
			
		||||
	DisplayName string `json:"displayname"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
type RespRegister struct {
 | 
			
		||||
	AccessToken  string `json:"access_token"`
 | 
			
		||||
@@ -125,6 +140,16 @@ type RespSync struct {
 | 
			
		||||
		Events []Event `json:"events"`
 | 
			
		||||
	} `json:"presence"`
 | 
			
		||||
	Rooms struct {
 | 
			
		||||
		Leave map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event `json:"events"`
 | 
			
		||||
			} `json:"state"`
 | 
			
		||||
			Timeline struct {
 | 
			
		||||
				Events    []Event `json:"events"`
 | 
			
		||||
				Limited   bool    `json:"limited"`
 | 
			
		||||
				PrevBatch string  `json:"prev_batch"`
 | 
			
		||||
			} `json:"timeline"`
 | 
			
		||||
		} `json:"leave"`
 | 
			
		||||
		Join map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event `json:"events"`
 | 
			
		||||
@@ -142,3 +167,10 @@ type RespSync struct {
 | 
			
		||||
		} `json:"invite"`
 | 
			
		||||
	} `json:"rooms"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RespTurnServer struct {
 | 
			
		||||
	Username string   `json:"username"`
 | 
			
		||||
	Password string   `json:"password"`
 | 
			
		||||
	TTL      int      `json:"ttl"`
 | 
			
		||||
	URIs     []string `json:"uris"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/matrix-org/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/matrix-org/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,7 +13,7 @@ func (room Room) UpdateState(event *Event) {
 | 
			
		||||
	if !exists {
 | 
			
		||||
		room.State[event.Type] = make(map[string]*Event)
 | 
			
		||||
	}
 | 
			
		||||
	room.State[event.Type][event.StateKey] = event
 | 
			
		||||
	room.State[event.Type][*event.StateKey] = event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/matrix-org/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/matrix-org/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -73,6 +73,16 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error)
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Leave {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.Timeline.Events {
 | 
			
		||||
			if event.StateKey != nil {
 | 
			
		||||
				event.RoomID = roomID
 | 
			
		||||
				room.UpdateState(&event)
 | 
			
		||||
				s.notifyListeners(&event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +112,7 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool
 | 
			
		||||
	for roomID, roomData := range resp.Rooms.Join {
 | 
			
		||||
		for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
 | 
			
		||||
			e := roomData.Timeline.Events[i]
 | 
			
		||||
			if e.Type == "m.room.member" && e.StateKey == s.UserID {
 | 
			
		||||
			if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
 | 
			
		||||
				m := e.Content["membership"]
 | 
			
		||||
				mship, ok := m.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							@@ -282,7 +282,7 @@
 | 
			
		||||
			"importpath": "github.com/lrstanley/girc",
 | 
			
		||||
			"repository": "https://github.com/lrstanley/girc",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "055075db54ebd311be5946efb3f62502846089ff",
 | 
			
		||||
			"revision": "5dff93b5453c1b2ac8382c9a38881635f47bba0e",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
@@ -290,7 +290,7 @@
 | 
			
		||||
			"importpath": "github.com/matrix-org/gomatrix",
 | 
			
		||||
			"repository": "https://github.com/matrix-org/gomatrix",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "812dcb5515581023371efaa6a82750d997f50d57",
 | 
			
		||||
			"revision": "a7fc80c8060c2544fe5d4dae465b584f8e9b4e27",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user