Compare commits
	
		
			32 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | bb38a61f3b | ||
|   | c447647af9 | ||
|   | 1de64f3f61 | ||
|   | 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,9 +179,27 @@ func (b *Birc) Send(msg config.Message) (string, error) { | ||||
| 		msg.Text = buf.String() | ||||
| 	} | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			for _, f := range msg.Extra["file"] { | ||||
| 				fi := f.(config.FileInfo) | ||||
| 				if fi.URL != "" { | ||||
| 					msg.Text = fi.URL | ||||
| 				} | ||||
| 				b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} | ||||
| 			} | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// split long messages on messageLength, to avoid clipped messages #281 | ||||
| 	if b.Config.MessageSplit { | ||||
| 		msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength) | ||||
| 	} | ||||
| 	for _, text := range strings.Split(msg.Text, "\n") { | ||||
| 		input := []rune(text) | ||||
| 		if len(text) > b.Config.MessageLength { | ||||
| 			text = text[:b.Config.MessageLength] + " <message clipped>" | ||||
| 			text = string(input[:b.Config.MessageLength]) + " <message clipped>" | ||||
| 		} | ||||
| 		if len(b.Local) < b.Config.MessageQueue { | ||||
| 			if len(b.Local) == b.Config.MessageQueue-1 { | ||||
| @@ -299,11 +328,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | ||||
| 	rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host} | ||||
| 	flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event) | ||||
| 	msg := "" | ||||
| 	if event.Command == "CTCP_ACTION" { | ||||
| 		//	msg = event.Source.Name + " " | ||||
| 	if event.IsAction() { | ||||
| 		rmsg.Event = config.EVENT_USER_ACTION | ||||
| 	} | ||||
| 	msg += event.Trailing | ||||
| 	msg += event.StripAction() | ||||
| 	// strip IRC colors | ||||
| 	re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`) | ||||
| 	msg = re.ReplaceAllString(msg, "") | ||||
|   | ||||
| @@ -1,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.1" | ||||
| 	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