Compare commits
	
		
			30 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a7a4554a85 | ||
| 
						 | 
					6bd808ce91 | ||
| 
						 | 
					a5c143bc46 | ||
| 
						 | 
					87c9cac756 | ||
| 
						 | 
					6a047f8722 | ||
| 
						 | 
					6523494e83 | ||
| 
						 | 
					7c6ce8bb90 | ||
| 
						 | 
					dafbfe4021 | ||
| 
						 | 
					a4d5c94d9b | ||
| 
						 | 
					7119e378a7 | ||
| 
						 | 
					e1dc3032c1 | ||
| 
						 | 
					5de03b8921 | ||
| 
						 | 
					7631d43c48 | ||
| 
						 | 
					d0b2ee5c85 | ||
| 
						 | 
					8830a5a1df | ||
| 
						 | 
					ee87626a93 | ||
| 
						 | 
					9f15d38c1c | ||
| 
						 | 
					4a96a977c0 | ||
| 
						 | 
					9a95293bdf | ||
| 
						 | 
					0b3a06d263 | ||
| 
						 | 
					9a6249c4f5 | ||
| 
						 | 
					50bd51e461 | ||
| 
						 | 
					04f8013314 | ||
| 
						 | 
					a0aaf0057a | ||
| 
						 | 
					8e78b3e6be | ||
| 
						 | 
					57a503818d | ||
| 
						 | 
					25d2ff3e9b | ||
| 
						 | 
					31902d3e57 | ||
| 
						 | 
					16f3fa6bae | ||
| 
						 | 
					1f706673cf | 
@@ -1,7 +1,7 @@
 | 
			
		||||
# matterbridge
 | 
			
		||||
Click on one of the badges below to join the chat   
 | 
			
		||||
 | 
			
		||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) 
 | 
			
		||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org) [](https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA) [](https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e) [](https://inverse.chat) [](https://www.twitch.tv/matterbridge)
 | 
			
		||||
 | 
			
		||||
[](https://github.com/42wim/matterbridge/releases/latest) [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
 | 
			
		||||
 | 
			
		||||
@@ -48,13 +48,14 @@ Accounts to one of the supported bridges
 | 
			
		||||
* [Rocket.chat](https://rocket.chat)
 | 
			
		||||
* [Matrix](https://matrix.org)
 | 
			
		||||
* [Steam](https://store.steampowered.com/)
 | 
			
		||||
* [Twitch](https://twitch.tv)
 | 
			
		||||
 | 
			
		||||
# Screenshots
 | 
			
		||||
See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
# Installing
 | 
			
		||||
## Binaries
 | 
			
		||||
* Latest stable release [v1.6.2](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Latest stable release [v1.7.0](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)  
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,7 @@ type Protocol struct {
 | 
			
		||||
	BindAddress            string // mattermost, slack // DEPRECATED
 | 
			
		||||
	Buffer                 int    // api
 | 
			
		||||
	Charset                string // irc
 | 
			
		||||
	Debug                  bool   // general
 | 
			
		||||
	EditSuffix             string // mattermost, slack, discord, telegram, gitter
 | 
			
		||||
	EditDisable            bool   // mattermost, slack, discord, telegram, gitter
 | 
			
		||||
	IconURL                string // mattermost, slack
 | 
			
		||||
 
 | 
			
		||||
@@ -323,7 +323,7 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
 | 
			
		||||
	if event.Source.Name == b.Nick {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
			
		||||
	rmsg := config.Message{Username: event.Source.Name, Channel: strings.ToLower(event.Params[0]), Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
 | 
			
		||||
	flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event)
 | 
			
		||||
	msg := ""
 | 
			
		||||
	if event.IsAction() {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	matrix "github.com/matrix-org/gomatrix"
 | 
			
		||||
	matrix "github.com/matterbridge/gomatrix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Bmatrix struct {
 | 
			
		||||
@@ -75,16 +75,26 @@ func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
	flog.Debugf("Receiving %#v", msg)
 | 
			
		||||
	channel := b.getRoomID(msg.Channel)
 | 
			
		||||
	// ignore delete messages
 | 
			
		||||
	if msg.Event == config.EVENT_MSG_DELETE {
 | 
			
		||||
		return "", nil
 | 
			
		||||
		if msg.ID == "" {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := b.mc.RedactEvent(channel, msg.ID, &matrix.ReqRedact{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return resp.EventID, err
 | 
			
		||||
	}
 | 
			
		||||
	channel := b.getRoomID(msg.Channel)
 | 
			
		||||
	flog.Debugf("Sending to channel %s", channel)
 | 
			
		||||
	if msg.Event == config.EVENT_USER_ACTION {
 | 
			
		||||
		b.mc.SendMessageEvent(channel, "m.room.message",
 | 
			
		||||
		resp, err := b.mc.SendMessageEvent(channel, "m.room.message",
 | 
			
		||||
			matrix.TextMessage{"m.emote", msg.Username + msg.Text})
 | 
			
		||||
		return "", nil
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return resp.EventID, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
@@ -124,8 +134,11 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
	return "", nil
 | 
			
		||||
	resp, err := b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return resp.EventID, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) getRoomID(channel string) string {
 | 
			
		||||
@@ -138,58 +151,11 @@ func (b *Bmatrix) getRoomID(channel string) string {
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
	syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
 | 
			
		||||
	syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
 | 
			
		||||
		flog.Debugf("Received: %#v", ev)
 | 
			
		||||
		if (ev.Content["msgtype"].(string) == "m.text" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.notice" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.emote" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.file" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.video") && ev.Sender != b.UserID {
 | 
			
		||||
			b.RLock()
 | 
			
		||||
			channel, ok := b.RoomMap[ev.RoomID]
 | 
			
		||||
			b.RUnlock()
 | 
			
		||||
			if !ok {
 | 
			
		||||
				flog.Debugf("Unknown room %s", ev.RoomID)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			username := ev.Sender[1:]
 | 
			
		||||
			if b.Config.NoHomeServerSuffix {
 | 
			
		||||
				re := regexp.MustCompile("(.*?):.*")
 | 
			
		||||
				username = re.ReplaceAllString(username, `$1`)
 | 
			
		||||
			}
 | 
			
		||||
			rmsg := config.Message{Username: username, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account, UserID: ev.Sender}
 | 
			
		||||
			if ev.Content["msgtype"].(string) == "m.emote" {
 | 
			
		||||
				rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
			}
 | 
			
		||||
			if ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
				ev.Content["msgtype"].(string) == "m.video" ||
 | 
			
		||||
				ev.Content["msgtype"].(string) == "m.file" {
 | 
			
		||||
				flog.Debugf("ev: %#v", ev)
 | 
			
		||||
				rmsg.Extra = make(map[string][]interface{})
 | 
			
		||||
				url := ev.Content["url"].(string)
 | 
			
		||||
				url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1)
 | 
			
		||||
				info := ev.Content["info"].(map[string]interface{})
 | 
			
		||||
				size := info["size"].(float64)
 | 
			
		||||
				name := ev.Content["body"].(string)
 | 
			
		||||
				flog.Debugf("trying to download %#v with size %#v", name, size)
 | 
			
		||||
				if size <= float64(b.General.MediaDownloadSize) {
 | 
			
		||||
					data, err := helper.DownloadFile(url)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						flog.Errorf("download %s failed %#v", url, err)
 | 
			
		||||
					} else {
 | 
			
		||||
						flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url))
 | 
			
		||||
						rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{Name: name, Data: data})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				rmsg.Text = ""
 | 
			
		||||
			}
 | 
			
		||||
			flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
 | 
			
		||||
			b.Remote <- rmsg
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	syncer.OnEventType("m.room.redaction", b.handleEvent)
 | 
			
		||||
	syncer.OnEventType("m.room.message", b.handleEvent)
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			if err := b.mc.Sync(); err != nil {
 | 
			
		||||
@@ -199,3 +165,73 @@ func (b *Bmatrix) handlematrix() error {
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) handleEvent(ev *matrix.Event) {
 | 
			
		||||
	flog.Debugf("Received: %#v", ev)
 | 
			
		||||
	if ev.Sender != b.UserID {
 | 
			
		||||
		b.RLock()
 | 
			
		||||
		channel, ok := b.RoomMap[ev.RoomID]
 | 
			
		||||
		b.RUnlock()
 | 
			
		||||
		if !ok {
 | 
			
		||||
			flog.Debugf("Unknown room %s", ev.RoomID)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		username := ev.Sender[1:]
 | 
			
		||||
		if b.Config.NoHomeServerSuffix {
 | 
			
		||||
			re := regexp.MustCompile("(.*?):.*")
 | 
			
		||||
			username = re.ReplaceAllString(username, `$1`)
 | 
			
		||||
		}
 | 
			
		||||
		var text string
 | 
			
		||||
		text, _ = ev.Content["body"].(string)
 | 
			
		||||
		rmsg := config.Message{Username: username, Text: text, Channel: channel, Account: b.Account, UserID: ev.Sender}
 | 
			
		||||
		rmsg.ID = ev.ID
 | 
			
		||||
		if ev.Type == "m.room.redaction" {
 | 
			
		||||
			rmsg.Event = config.EVENT_MSG_DELETE
 | 
			
		||||
			rmsg.ID = ev.Redacts
 | 
			
		||||
			rmsg.Text = config.EVENT_MSG_DELETE
 | 
			
		||||
			b.Remote <- rmsg
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if ev.Content["msgtype"].(string) == "m.emote" {
 | 
			
		||||
			rmsg.Event = config.EVENT_USER_ACTION
 | 
			
		||||
		}
 | 
			
		||||
		if ev.Content["msgtype"] != nil && ev.Content["msgtype"].(string) == "m.image" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.video" ||
 | 
			
		||||
			ev.Content["msgtype"].(string) == "m.file" {
 | 
			
		||||
			flog.Debugf("ev: %#v", ev)
 | 
			
		||||
			rmsg.Extra = make(map[string][]interface{})
 | 
			
		||||
			url := ev.Content["url"].(string)
 | 
			
		||||
			url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1)
 | 
			
		||||
			info := ev.Content["info"].(map[string]interface{})
 | 
			
		||||
			size := info["size"].(float64)
 | 
			
		||||
			name := ev.Content["body"].(string)
 | 
			
		||||
			// check if we have an image uploaded without extension
 | 
			
		||||
			if !strings.Contains(name, ".") {
 | 
			
		||||
				if ev.Content["msgtype"].(string) == "m.image" {
 | 
			
		||||
					if mtype, ok := ev.Content["mimetype"].(string); ok {
 | 
			
		||||
						mext, _ := mime.ExtensionsByType(mtype)
 | 
			
		||||
						if len(mext) > 0 {
 | 
			
		||||
							name = name + mext[0]
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						// just a default .png extension if we don't have mime info
 | 
			
		||||
						name = name + ".png"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			flog.Debugf("trying to download %#v with size %#v", name, size)
 | 
			
		||||
			if size <= float64(b.General.MediaDownloadSize) {
 | 
			
		||||
				data, err := helper.DownloadFile(url)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					flog.Errorf("download %s failed %#v", url, err)
 | 
			
		||||
				} else {
 | 
			
		||||
					flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url))
 | 
			
		||||
					rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{Name: name, Data: data})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			rmsg.Text = ""
 | 
			
		||||
		}
 | 
			
		||||
		flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
 | 
			
		||||
		b.Remote <- rmsg
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	log "github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/matterbridge/slack"
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
	"html"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -107,7 +107,7 @@ func (b *Bslack) Disconnect() error {
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
	// we can only join channels using the API
 | 
			
		||||
	if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" {
 | 
			
		||||
	if b.sc != nil {
 | 
			
		||||
		if strings.HasPrefix(b.Config.Token, "xoxb") {
 | 
			
		||||
			// TODO check if bot has already joined channel
 | 
			
		||||
			return nil
 | 
			
		||||
@@ -309,9 +309,11 @@ func (b *Bslack) handleSlack() {
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
	for msg := range b.rtm.IncomingEvents {
 | 
			
		||||
		if msg.Type != "user_typing" && msg.Type != "latency_report" {
 | 
			
		||||
			flog.Debugf("Receiving from slackclient %#v", msg.Data)
 | 
			
		||||
		}
 | 
			
		||||
		switch ev := msg.Data.(type) {
 | 
			
		||||
		case *slack.MessageEvent:
 | 
			
		||||
			flog.Debugf("Receiving from slackclient %#v", ev)
 | 
			
		||||
			if len(ev.Attachments) > 0 {
 | 
			
		||||
				// skip messages we made ourselves
 | 
			
		||||
				if ev.Attachments[0].CallbackID == "matterbridge" {
 | 
			
		||||
@@ -394,6 +396,8 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
 | 
			
		||||
			}
 | 
			
		||||
		case *slack.InvalidAuthEvent:
 | 
			
		||||
			flog.Fatalf("Invalid Token %#v", ev)
 | 
			
		||||
		case *slack.ConnectionErrorEvent:
 | 
			
		||||
			flog.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
 | 
			
		||||
		TLSConfig: tc,
 | 
			
		||||
 | 
			
		||||
		//StartTLS:      false,
 | 
			
		||||
		Debug:                        true,
 | 
			
		||||
		Debug:                        b.General.Debug,
 | 
			
		||||
		Session:                      true,
 | 
			
		||||
		Status:                       "",
 | 
			
		||||
		StatusMessage:                "",
 | 
			
		||||
@@ -166,7 +166,7 @@ func (b *Bxmpp) handleXmpp() error {
 | 
			
		||||
				if len(s) == 2 {
 | 
			
		||||
					nick = s[1]
 | 
			
		||||
				}
 | 
			
		||||
				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" {
 | 
			
		||||
				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" && !strings.Contains(v.Text, "</subject>") {
 | 
			
		||||
					rmsg := config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote}
 | 
			
		||||
					rmsg.Text, ok = b.replaceAction(rmsg.Text)
 | 
			
		||||
					if ok {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,25 @@
 | 
			
		||||
# v1.7.0
 | 
			
		||||
## New features
 | 
			
		||||
* matrix: Add support for deleting messages from/to matrix (matrix). Closes #320
 | 
			
		||||
* xmpp: Ignore <subject> messages (xmpp). #272
 | 
			
		||||
* irc: Add twitch support (irc) to README / wiki
 | 
			
		||||
 | 
			
		||||
## Bugfix
 | 
			
		||||
* general: Change RemoteNickFormat replacement order. Closes #336
 | 
			
		||||
* general: Make edits/delete work for bridges that gets reused. Closes #342
 | 
			
		||||
* general: Lowercase irc channels in config. Closes #348
 | 
			
		||||
* matrix: Fix possible panics (matrix). Closes #333
 | 
			
		||||
* matrix: Add an extension to images without one (matrix). #331
 | 
			
		||||
* api: Obey the Gateway value from the json (api). Closes #344
 | 
			
		||||
* xmpp: Print only debug messages when specified (xmpp). Closes #345
 | 
			
		||||
* xmpp: Allow xmpp to receive the extra messages (file uploads) when text is empty. #295
 | 
			
		||||
 | 
			
		||||
# v1.6.3
 | 
			
		||||
## Bugfix
 | 
			
		||||
* slack: Fix connection issues
 | 
			
		||||
* slack: Add more debug messages
 | 
			
		||||
* irc: Convert received IRC channel names to lowercase. Fixes #329 (#330)
 | 
			
		||||
 | 
			
		||||
# v1.6.2
 | 
			
		||||
## Bugfix
 | 
			
		||||
* mattermost: Crashes while connecting to Mattermost (regression). Closes #327
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								docker/arm/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								docker/arm/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
FROM cmosh/alpine-arm:edge
 | 
			
		||||
ENTRYPOINT ["/bin/matterbridge"]
 | 
			
		||||
 | 
			
		||||
COPY . /go/src/github.com/42wim/matterbridge
 | 
			
		||||
RUN apk update && apk add go git gcc musl-dev ca-certificates \
 | 
			
		||||
        && cd /go/src/github.com/42wim/matterbridge \
 | 
			
		||||
        && export GOPATH=/go \
 | 
			
		||||
        && go get \
 | 
			
		||||
        && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
 | 
			
		||||
        && rm -rf /go \
 | 
			
		||||
        && apk del --purge git go gcc musl-dev
 | 
			
		||||
@@ -29,8 +29,9 @@ type Gateway struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BrMsgID struct {
 | 
			
		||||
	br *bridge.Bridge
 | 
			
		||||
	ID string
 | 
			
		||||
	br        *bridge.Bridge
 | 
			
		||||
	ID        string
 | 
			
		||||
	ChannelID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg config.Gateway, r *Router) *Gateway {
 | 
			
		||||
@@ -93,6 +94,10 @@ func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
 | 
			
		||||
		if isApi(br.Account) {
 | 
			
		||||
			br.Channel = "api"
 | 
			
		||||
		}
 | 
			
		||||
		// make sure to lowercase irc channels in config #348
 | 
			
		||||
		if strings.HasPrefix(br.Account, "irc.") {
 | 
			
		||||
			br.Channel = strings.ToLower(br.Channel)
 | 
			
		||||
		}
 | 
			
		||||
		ID := br.Channel + br.Account
 | 
			
		||||
		if _, ok := gw.Channels[ID]; !ok {
 | 
			
		||||
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
 | 
			
		||||
@@ -118,6 +123,12 @@ func (gw *Gateway) mapChannels() error {
 | 
			
		||||
 | 
			
		||||
func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
 | 
			
		||||
	var channels []config.ChannelInfo
 | 
			
		||||
 | 
			
		||||
	// for messages received from the api check that the gateway is the specified one
 | 
			
		||||
	if msg.Protocol == "api" && gw.Name != msg.Gateway {
 | 
			
		||||
		return channels
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if source channel is in only, do nothing
 | 
			
		||||
	for _, channel := range gw.Channels {
 | 
			
		||||
		// lookup the channel from the message
 | 
			
		||||
@@ -159,7 +170,8 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
			dest.Protocol != "slack" &&
 | 
			
		||||
			dest.Protocol != "mattermost" &&
 | 
			
		||||
			dest.Protocol != "telegram" &&
 | 
			
		||||
			dest.Protocol != "matrix" {
 | 
			
		||||
			dest.Protocol != "matrix" &&
 | 
			
		||||
			dest.Protocol != "xmpp" {
 | 
			
		||||
			if msg.Text == "" {
 | 
			
		||||
				return brMsgIDs
 | 
			
		||||
			}
 | 
			
		||||
@@ -190,7 +202,9 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
 | 
			
		||||
			IDs := res.([]*BrMsgID)
 | 
			
		||||
			for _, id := range IDs {
 | 
			
		||||
				if dest.Protocol == id.br.Protocol {
 | 
			
		||||
				// check protocol, bridge name and channelname
 | 
			
		||||
				// for people that reuse the same bridge multiple times. see #342
 | 
			
		||||
				if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
 | 
			
		||||
					msg.ID = id.ID
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -205,7 +219,7 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
 | 
			
		||||
		}
 | 
			
		||||
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
 | 
			
		||||
		if mID != "" {
 | 
			
		||||
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID})
 | 
			
		||||
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return brMsgIDs
 | 
			
		||||
@@ -284,9 +298,9 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
 | 
			
		||||
		}
 | 
			
		||||
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
 | 
			
		||||
	}
 | 
			
		||||
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
 | 
			
		||||
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
 | 
			
		||||
	return nick
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -318,7 +332,11 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
 | 
			
		||||
		}
 | 
			
		||||
		msg.Text = re.ReplaceAllString(msg.Text, replace)
 | 
			
		||||
	}
 | 
			
		||||
	msg.Gateway = gw.Name
 | 
			
		||||
 | 
			
		||||
	// messages from api have Gateway specified, don't overwrite
 | 
			
		||||
	if msg.Protocol != "api" {
 | 
			
		||||
		msg.Gateway = gw.Name
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (gw *Gateway) handleFiles(msg *config.Message) {
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.6.2"
 | 
			
		||||
	version = "1.7.0"
 | 
			
		||||
	githash string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +43,7 @@ func main() {
 | 
			
		||||
		log.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
 | 
			
		||||
	}
 | 
			
		||||
	cfg := config.NewConfig(*flagConfig)
 | 
			
		||||
	cfg.General.Debug = *flagDebug
 | 
			
		||||
	r, err := gateway.NewRouter(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Starting gateway failed: %s", err)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										201
									
								
								vendor/github.com/matterbridge/gomatrix/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/matterbridge/gomatrix/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "{}"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright {yyyy} {name of copyright owner}
 | 
			
		||||
 | 
			
		||||
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
   you may not use this file except in compliance with the License.
 | 
			
		||||
   You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
       http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
   Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
   See the License for the specific language governing permissions and
 | 
			
		||||
   limitations under the License.
 | 
			
		||||
							
								
								
									
										703
									
								
								vendor/github.com/matterbridge/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										703
									
								
								vendor/github.com/matterbridge/gomatrix/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,703 @@
 | 
			
		||||
// Package gomatrix implements the Matrix Client-Server API.
 | 
			
		||||
//
 | 
			
		||||
// Specification can be found at http://matrix.org/docs/spec/client_server/r0.2.0.html
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client represents a Matrix client.
 | 
			
		||||
type Client struct {
 | 
			
		||||
	HomeserverURL *url.URL     // The base homeserver URL
 | 
			
		||||
	Prefix        string       // The API prefix eg '/_matrix/client/r0'
 | 
			
		||||
	UserID        string       // The user ID of the client. Used for forming HTTP paths which use the client's user ID.
 | 
			
		||||
	AccessToken   string       // The access_token for the client.
 | 
			
		||||
	Client        *http.Client // The underlying HTTP client which will be used to make HTTP requests.
 | 
			
		||||
	Syncer        Syncer       // The thing which can process /sync responses
 | 
			
		||||
	Store         Storer       // The thing which can store rooms/tokens/ids
 | 
			
		||||
 | 
			
		||||
	// The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty,
 | 
			
		||||
	// no user_id parameter will be sent.
 | 
			
		||||
	// See http://matrix.org/docs/spec/application_service/unstable.html#identity-assertion
 | 
			
		||||
	AppServiceUserID string
 | 
			
		||||
 | 
			
		||||
	syncingMutex sync.Mutex // protects syncingID
 | 
			
		||||
	syncingID    uint32     // Identifies the current Sync. Only one Sync can be active at any given time.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
 | 
			
		||||
type HTTPError struct {
 | 
			
		||||
	WrappedError error
 | 
			
		||||
	Message      string
 | 
			
		||||
	Code         int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e HTTPError) Error() string {
 | 
			
		||||
	var wrappedErrMsg string
 | 
			
		||||
	if e.WrappedError != nil {
 | 
			
		||||
		wrappedErrMsg = e.WrappedError.Error()
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("msg=%s code=%d wrapped=%s", e.Message, e.Code, wrappedErrMsg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildURL builds a URL with the Client's homserver/prefix/access_token set already.
 | 
			
		||||
func (cli *Client) BuildURL(urlPath ...string) string {
 | 
			
		||||
	ps := []string{cli.Prefix}
 | 
			
		||||
	for _, p := range urlPath {
 | 
			
		||||
		ps = append(ps, p)
 | 
			
		||||
	}
 | 
			
		||||
	return cli.BuildBaseURL(ps...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildBaseURL builds a URL with the Client's homeserver/access_token set already. You must
 | 
			
		||||
// supply the prefix in the path.
 | 
			
		||||
func (cli *Client) BuildBaseURL(urlPath ...string) string {
 | 
			
		||||
	// copy the URL. Purposefully ignore error as the input is from a valid URL already
 | 
			
		||||
	hsURL, _ := url.Parse(cli.HomeserverURL.String())
 | 
			
		||||
	parts := []string{hsURL.Path}
 | 
			
		||||
	parts = append(parts, urlPath...)
 | 
			
		||||
	hsURL.Path = path.Join(parts...)
 | 
			
		||||
	query := hsURL.Query()
 | 
			
		||||
	if cli.AccessToken != "" {
 | 
			
		||||
		query.Set("access_token", cli.AccessToken)
 | 
			
		||||
	}
 | 
			
		||||
	if cli.AppServiceUserID != "" {
 | 
			
		||||
		query.Set("user_id", cli.AppServiceUserID)
 | 
			
		||||
	}
 | 
			
		||||
	hsURL.RawQuery = query.Encode()
 | 
			
		||||
	return hsURL.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already.
 | 
			
		||||
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
 | 
			
		||||
	u, _ := url.Parse(cli.BuildURL(urlPath...))
 | 
			
		||||
	q := u.Query()
 | 
			
		||||
	for k, v := range urlQuery {
 | 
			
		||||
		q.Set(k, v)
 | 
			
		||||
	}
 | 
			
		||||
	u.RawQuery = q.Encode()
 | 
			
		||||
	return u.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCredentials sets the user ID and access token on this client instance.
 | 
			
		||||
func (cli *Client) SetCredentials(userID, accessToken string) {
 | 
			
		||||
	cli.AccessToken = accessToken
 | 
			
		||||
	cli.UserID = userID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearCredentials removes the user ID and access token on this client instance.
 | 
			
		||||
func (cli *Client) ClearCredentials() {
 | 
			
		||||
	cli.AccessToken = ""
 | 
			
		||||
	cli.UserID = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the
 | 
			
		||||
// error will be nil.
 | 
			
		||||
//
 | 
			
		||||
// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine.
 | 
			
		||||
// Fatal sync errors can be caused by:
 | 
			
		||||
//   - The failure to create a filter.
 | 
			
		||||
//   - Client.Syncer.OnFailedSync returning an error in response to a failed sync.
 | 
			
		||||
//   - Client.Syncer.ProcessResponse returning an error.
 | 
			
		||||
// If you wish to continue retrying in spite of these fatal errors, call Sync() again.
 | 
			
		||||
func (cli *Client) Sync() error {
 | 
			
		||||
	// Mark the client as syncing.
 | 
			
		||||
	// We will keep syncing until the syncing state changes. Either because
 | 
			
		||||
	// Sync is called or StopSync is called.
 | 
			
		||||
	syncingID := cli.incrementSyncingID()
 | 
			
		||||
	nextBatch := cli.Store.LoadNextBatch(cli.UserID)
 | 
			
		||||
	filterID := cli.Store.LoadFilterID(cli.UserID)
 | 
			
		||||
	if filterID == "" {
 | 
			
		||||
		filterJSON := cli.Syncer.GetFilterJSON(cli.UserID)
 | 
			
		||||
		resFilter, err := cli.CreateFilter(filterJSON)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		filterID = resFilter.FilterID
 | 
			
		||||
		cli.Store.SaveFilterID(cli.UserID, filterID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		resSync, err := cli.SyncRequest(30000, nextBatch, filterID, false, "")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			duration, err2 := cli.Syncer.OnFailedSync(resSync, err)
 | 
			
		||||
			if err2 != nil {
 | 
			
		||||
				return err2
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(duration)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check that the syncing state hasn't changed
 | 
			
		||||
		// Either because we've stopped syncing or another sync has been started.
 | 
			
		||||
		// We discard the response from our sync.
 | 
			
		||||
		if cli.getSyncingID() != syncingID {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Save the token now *before* processing it. This means it's possible
 | 
			
		||||
		// to not process some events, but it means that we won't get constantly stuck processing
 | 
			
		||||
		// a malformed/buggy event which keeps making us panic.
 | 
			
		||||
		cli.Store.SaveNextBatch(cli.UserID, resSync.NextBatch)
 | 
			
		||||
		if err = cli.Syncer.ProcessResponse(resSync, nextBatch); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nextBatch = resSync.NextBatch
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *Client) incrementSyncingID() uint32 {
 | 
			
		||||
	cli.syncingMutex.Lock()
 | 
			
		||||
	defer cli.syncingMutex.Unlock()
 | 
			
		||||
	cli.syncingID++
 | 
			
		||||
	return cli.syncingID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *Client) getSyncingID() uint32 {
 | 
			
		||||
	cli.syncingMutex.Lock()
 | 
			
		||||
	defer cli.syncingMutex.Unlock()
 | 
			
		||||
	return cli.syncingID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StopSync stops the ongoing sync started by Sync.
 | 
			
		||||
func (cli *Client) StopSync() {
 | 
			
		||||
	// Advance the syncing state so that any running Syncs will terminate.
 | 
			
		||||
	cli.incrementSyncingID()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRequest makes a JSON HTTP request to the given URL.
 | 
			
		||||
// If "resBody" is not nil, the response body will be json.Unmarshalled into it.
 | 
			
		||||
//
 | 
			
		||||
// Returns the HTTP body as bytes on 2xx with a nil error. Returns an error if the response is not 2xx along
 | 
			
		||||
// with the HTTP body bytes if it got that far. This error is an HTTPError which includes the returned
 | 
			
		||||
// HTTP status code and possibly a RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
 | 
			
		||||
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
 | 
			
		||||
	var req *http.Request
 | 
			
		||||
	var err error
 | 
			
		||||
	if reqBody != nil {
 | 
			
		||||
		var jsonStr []byte
 | 
			
		||||
		jsonStr, err = json.Marshal(reqBody)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr))
 | 
			
		||||
	} else {
 | 
			
		||||
		req, err = http.NewRequest(method, httpURL, nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Content-Type", "application/json")
 | 
			
		||||
	res, err := cli.Client.Do(req)
 | 
			
		||||
	if res != nil {
 | 
			
		||||
		defer res.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	contents, err := ioutil.ReadAll(res.Body)
 | 
			
		||||
	if res.StatusCode/100 != 2 { // not 2xx
 | 
			
		||||
		var wrap error
 | 
			
		||||
		var respErr RespError
 | 
			
		||||
		if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" {
 | 
			
		||||
			wrap = respErr
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we failed to decode as RespError, don't just drop the HTTP body, include it in the
 | 
			
		||||
		// HTTP error instead (e.g proxy errors which return HTML).
 | 
			
		||||
		msg := "Failed to " + method + " JSON to " + req.URL.Path
 | 
			
		||||
		if wrap == nil {
 | 
			
		||||
			msg = msg + ": " + string(contents)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return contents, HTTPError{
 | 
			
		||||
			Code:         res.StatusCode,
 | 
			
		||||
			Message:      msg,
 | 
			
		||||
			WrappedError: wrap,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resBody != nil {
 | 
			
		||||
		if err = json.Unmarshal(contents, &resBody); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return contents, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
 | 
			
		||||
func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("user", cli.UserID, "filter")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, &filter, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SyncRequest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
 | 
			
		||||
func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bool, setPresence string) (resp *RespSync, err error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"timeout": strconv.Itoa(timeout),
 | 
			
		||||
	}
 | 
			
		||||
	if since != "" {
 | 
			
		||||
		query["since"] = since
 | 
			
		||||
	}
 | 
			
		||||
	if filterID != "" {
 | 
			
		||||
		query["filter"] = filterID
 | 
			
		||||
	}
 | 
			
		||||
	if setPresence != "" {
 | 
			
		||||
		query["set_presence"] = setPresence
 | 
			
		||||
	}
 | 
			
		||||
	if fullState {
 | 
			
		||||
		query["full_state"] = "true"
 | 
			
		||||
	}
 | 
			
		||||
	urlPath := cli.BuildURLWithQuery([]string{"sync"}, query)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
 | 
			
		||||
	var bodyBytes []byte
 | 
			
		||||
	bodyBytes, err = cli.MakeRequest("POST", u, req, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		httpErr, ok := err.(HTTPError)
 | 
			
		||||
		if !ok { // network error
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if httpErr.Code == 401 {
 | 
			
		||||
			// body should be RespUserInteractive, if it isn't, fail with the error
 | 
			
		||||
			err = json.Unmarshal(bodyBytes, &uiaResp)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// body should be RespRegister
 | 
			
		||||
	err = json.Unmarshal(bodyBytes, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
//
 | 
			
		||||
// Registers with kind=user. For kind=guest, see RegisterGuest.
 | 
			
		||||
func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
 | 
			
		||||
	u := cli.BuildURL("register")
 | 
			
		||||
	return cli.register(u, req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
// with kind=guest.
 | 
			
		||||
//
 | 
			
		||||
// For kind=user, see Register.
 | 
			
		||||
func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"kind": "guest",
 | 
			
		||||
	}
 | 
			
		||||
	u := cli.BuildURLWithQuery([]string{"register"}, query)
 | 
			
		||||
	return cli.register(u, req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth
 | 
			
		||||
//
 | 
			
		||||
// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration
 | 
			
		||||
// this way. If the homeserver does not, an error is returned.
 | 
			
		||||
//
 | 
			
		||||
// This does not set credentials on the client instance. See SetCredentials() instead.
 | 
			
		||||
//
 | 
			
		||||
// 	res, err := cli.RegisterDummy(&gomatrix.ReqRegister{
 | 
			
		||||
//		Username: "alice",
 | 
			
		||||
//		Password: "wonderland",
 | 
			
		||||
//	})
 | 
			
		||||
//  if err != nil {
 | 
			
		||||
// 		panic(err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	token := res.AccessToken
 | 
			
		||||
func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
 | 
			
		||||
	res, uia, err := cli.Register(req)
 | 
			
		||||
	if err != nil && uia == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if uia != nil && uia.HasSingleStageFlow("m.login.dummy") {
 | 
			
		||||
		req.Auth = struct {
 | 
			
		||||
			Type    string `json:"type"`
 | 
			
		||||
			Session string `json:"session,omitempty"`
 | 
			
		||||
		}{"m.login.dummy", uia.Session}
 | 
			
		||||
		res, _, err = cli.Register(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if res == nil {
 | 
			
		||||
		return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?")
 | 
			
		||||
	}
 | 
			
		||||
	return res, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Login a user to the homeserver according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
 | 
			
		||||
// This does not set credentials on this client instance. See SetCredentials() instead.
 | 
			
		||||
func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("login")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
 | 
			
		||||
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
 | 
			
		||||
func (cli *Client) Logout() (resp *RespLogout, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("logout")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
 | 
			
		||||
func (cli *Client) Versions() (resp *RespVersions, err error) {
 | 
			
		||||
	urlPath := cli.BuildBaseURL("_matrix", "client", "versions")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias
 | 
			
		||||
//
 | 
			
		||||
// If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will
 | 
			
		||||
// be JSON encoded and used as the request body.
 | 
			
		||||
func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{}) (resp *RespJoinRoom, err error) {
 | 
			
		||||
	var urlPath string
 | 
			
		||||
	if serverName != "" {
 | 
			
		||||
		urlPath = cli.BuildURLWithQuery([]string{"join", roomIDorAlias}, map[string]string{
 | 
			
		||||
			"server_name": serverName,
 | 
			
		||||
		})
 | 
			
		||||
	} else {
 | 
			
		||||
		urlPath = cli.BuildURL("join", roomIDorAlias)
 | 
			
		||||
	}
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, content, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", mxid, "displayname")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
func (cli *Client) SetDisplayName(displayName string) (err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
 | 
			
		||||
	s := struct {
 | 
			
		||||
		DisplayName string `json:"displayname"`
 | 
			
		||||
	}{displayName}
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url
 | 
			
		||||
func (cli *Client) GetAvatarURL() (url string, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
 | 
			
		||||
	s := struct {
 | 
			
		||||
		AvatarURL string `json:"avatar_url"`
 | 
			
		||||
	}{}
 | 
			
		||||
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.AvatarURL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url
 | 
			
		||||
func (cli *Client) SetAvatarURL(url string) (err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
 | 
			
		||||
	s := struct {
 | 
			
		||||
		AvatarURL string `json:"avatar_url"`
 | 
			
		||||
	}{url}
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
 | 
			
		||||
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
 | 
			
		||||
func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) {
 | 
			
		||||
	txnID := txnID()
 | 
			
		||||
	urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
 | 
			
		||||
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
 | 
			
		||||
func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendText sends an m.room.message event into the given room with a msgtype of m.text
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
 | 
			
		||||
func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		TextMessage{"m.text", text})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendImage sends an m.room.message event into the given room with a msgtype of m.image
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
 | 
			
		||||
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		ImageMessage{
 | 
			
		||||
			MsgType: "m.image",
 | 
			
		||||
			Body:    body,
 | 
			
		||||
			URL:     url,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		VideoMessage{
 | 
			
		||||
			MsgType: "m.video",
 | 
			
		||||
			Body:    body,
 | 
			
		||||
			URL:     url,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
 | 
			
		||||
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
 | 
			
		||||
	return cli.SendMessageEvent(roomID, "m.room.message",
 | 
			
		||||
		TextMessage{"m.notice", text})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
 | 
			
		||||
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
 | 
			
		||||
	txnID := txnID()
 | 
			
		||||
	urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", urlPath, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
//  resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{
 | 
			
		||||
//  	Preset: "public_chat",
 | 
			
		||||
//  })
 | 
			
		||||
//  fmt.Println("Room:", resp.RoomID)
 | 
			
		||||
func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("createRoom")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", urlPath, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
 | 
			
		||||
func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "leave")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
 | 
			
		||||
func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "forget")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
 | 
			
		||||
func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "invite")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint
 | 
			
		||||
func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "invite")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
 | 
			
		||||
func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "kick")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
 | 
			
		||||
func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "ban")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "unban")
 | 
			
		||||
	_, err = cli.MakeRequest("POST", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
 | 
			
		||||
	req := ReqTyping{Typing: typing, Timeout: timeout}
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
 | 
			
		||||
	_, err = cli.MakeRequest("PUT", u, req, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
 | 
			
		||||
// the HTTP response body, or return an error.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
 | 
			
		||||
func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", u, nil, outContent)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadLink uploads an HTTP URL and then returns an MXC URI.
 | 
			
		||||
func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
 | 
			
		||||
	res, err := cli.Client.Get(link)
 | 
			
		||||
	if res != nil {
 | 
			
		||||
		defer res.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
 | 
			
		||||
func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) {
 | 
			
		||||
	req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Content-Type", contentType)
 | 
			
		||||
	req.ContentLength = contentLength
 | 
			
		||||
	res, err := cli.Client.Do(req)
 | 
			
		||||
	if res != nil {
 | 
			
		||||
		defer res.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if res.StatusCode != 200 {
 | 
			
		||||
		contents, err := ioutil.ReadAll(res.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, HTTPError{
 | 
			
		||||
				Message: "Upload request failed - Failed to read response body: " + err.Error(),
 | 
			
		||||
				Code:    res.StatusCode,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil, HTTPError{
 | 
			
		||||
			Message: "Upload request failed: " + string(contents),
 | 
			
		||||
			Code:    res.StatusCode,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var m RespMediaUpload
 | 
			
		||||
	if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &m, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinedMembers returns a map of joined room members. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
//
 | 
			
		||||
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
 | 
			
		||||
// This API is primarily designed for application services which may want to efficiently look up joined members in a room.
 | 
			
		||||
func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) {
 | 
			
		||||
	u := cli.BuildURL("rooms", roomID, "joined_members")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", u, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinedRooms returns a list of rooms which the client is joined to. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
//
 | 
			
		||||
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
 | 
			
		||||
// This API is primarily designed for application services which may want to efficiently look up joined rooms.
 | 
			
		||||
func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
 | 
			
		||||
	u := cli.BuildURL("joined_rooms")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", u, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messages returns a list of message and state events for a room. It uses
 | 
			
		||||
// pagination query parameters to paginate history in the room.
 | 
			
		||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
 | 
			
		||||
func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
 | 
			
		||||
	query := map[string]string{
 | 
			
		||||
		"from": from,
 | 
			
		||||
		"dir":  string(dir),
 | 
			
		||||
	}
 | 
			
		||||
	if to != "" {
 | 
			
		||||
		query["to"] = to
 | 
			
		||||
	}
 | 
			
		||||
	if limit != 0 {
 | 
			
		||||
		query["limit"] = strconv.Itoa(limit)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TurnServer returns turn server details and credentials for the client to use when initiating calls.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
 | 
			
		||||
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
 | 
			
		||||
	urlPath := cli.BuildURL("voip", "turnServer")
 | 
			
		||||
	_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func txnID() string {
 | 
			
		||||
	return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient creates a new Matrix Client ready for syncing
 | 
			
		||||
func NewClient(homeserverURL, userID, accessToken string) (*Client, error) {
 | 
			
		||||
	hsURL, err := url.Parse(homeserverURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// By default, use an in-memory store which will never save filter ids / next batch tokens to disk.
 | 
			
		||||
	// The client will work with this storer: it just won't remember across restarts.
 | 
			
		||||
	// In practice, a database backend should be used.
 | 
			
		||||
	store := NewInMemoryStore()
 | 
			
		||||
	cli := Client{
 | 
			
		||||
		AccessToken:   accessToken,
 | 
			
		||||
		HomeserverURL: hsURL,
 | 
			
		||||
		UserID:        userID,
 | 
			
		||||
		Prefix:        "/_matrix/client/r0",
 | 
			
		||||
		Syncer:        NewDefaultSyncer(userID, store),
 | 
			
		||||
		Store:         store,
 | 
			
		||||
	}
 | 
			
		||||
	// By default, use the default HTTP client.
 | 
			
		||||
	cli.Client = http.DefaultClient
 | 
			
		||||
 | 
			
		||||
	return &cli, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										102
									
								
								vendor/github.com/matterbridge/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/matterbridge/gomatrix/events.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"html"
 | 
			
		||||
	"regexp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Event represents a single Matrix event.
 | 
			
		||||
type Event struct {
 | 
			
		||||
	StateKey  *string                `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
 | 
			
		||||
	Sender    string                 `json:"sender"`              // The user ID of the sender of the event
 | 
			
		||||
	Type      string                 `json:"type"`                // The event type
 | 
			
		||||
	Timestamp int64                  `json:"origin_server_ts"`    // The unix timestamp when this message was sent by the origin server
 | 
			
		||||
	ID        string                 `json:"event_id"`            // The unique ID of this event
 | 
			
		||||
	RoomID    string                 `json:"room_id"`             // The room the event was sent to. May be nil (e.g. for presence)
 | 
			
		||||
	Content   map[string]interface{} `json:"content"`             // The JSON content of the event.
 | 
			
		||||
	Redacts   string                 `json:"redacts,omitempty"`   // The event ID that was redacted if a m.room.redaction event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Body returns the value of the "body" key in the event content if it is
 | 
			
		||||
// present and is a string.
 | 
			
		||||
func (event *Event) Body() (body string, ok bool) {
 | 
			
		||||
	value, exists := event.Content["body"]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	body, ok = value.(string)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MessageType returns the value of the "msgtype" key in the event content if
 | 
			
		||||
// it is present and is a string.
 | 
			
		||||
func (event *Event) MessageType() (msgtype string, ok bool) {
 | 
			
		||||
	value, exists := event.Content["msgtype"]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	msgtype, ok = value.(string)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TextMessage is the contents of a Matrix formated message event.
 | 
			
		||||
type TextMessage struct {
 | 
			
		||||
	MsgType string `json:"msgtype"`
 | 
			
		||||
	Body    string `json:"body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
 | 
			
		||||
type ImageInfo struct {
 | 
			
		||||
	Height   uint   `json:"h,omitempty"`
 | 
			
		||||
	Width    uint   `json:"w,omitempty"`
 | 
			
		||||
	Mimetype string `json:"mimetype,omitempty"`
 | 
			
		||||
	Size     uint   `json:"size,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
type VideoInfo struct {
 | 
			
		||||
	Mimetype      string    `json:"mimetype,omitempty"`
 | 
			
		||||
	ThumbnailInfo ImageInfo `json:"thumbnail_info"`
 | 
			
		||||
	ThumbnailURL  string    `json:"thumbnail_url,omitempty"`
 | 
			
		||||
	Height        uint      `json:"h,omitempty"`
 | 
			
		||||
	Width         uint      `json:"w,omitempty"`
 | 
			
		||||
	Duration      uint      `json:"duration,omitempty"`
 | 
			
		||||
	Size          uint      `json:"size,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VideoMessage is an m.video  - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
 | 
			
		||||
type VideoMessage struct {
 | 
			
		||||
	MsgType string    `json:"msgtype"`
 | 
			
		||||
	Body    string    `json:"body"`
 | 
			
		||||
	URL     string    `json:"url"`
 | 
			
		||||
	Info    VideoInfo `json:"info"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageMessage is an m.image event
 | 
			
		||||
type ImageMessage struct {
 | 
			
		||||
	MsgType string    `json:"msgtype"`
 | 
			
		||||
	Body    string    `json:"body"`
 | 
			
		||||
	URL     string    `json:"url"`
 | 
			
		||||
	Info    ImageInfo `json:"info"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An HTMLMessage is the contents of a Matrix HTML formated message event.
 | 
			
		||||
type HTMLMessage struct {
 | 
			
		||||
	Body          string `json:"body"`
 | 
			
		||||
	MsgType       string `json:"msgtype"`
 | 
			
		||||
	Format        string `json:"format"`
 | 
			
		||||
	FormattedBody string `json:"formatted_body"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var htmlRegex = regexp.MustCompile("<[^<]+?>")
 | 
			
		||||
 | 
			
		||||
// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition
 | 
			
		||||
// to the provided HTML.
 | 
			
		||||
func GetHTMLMessage(msgtype, htmlText string) HTMLMessage {
 | 
			
		||||
	return HTMLMessage{
 | 
			
		||||
		Body:          html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
 | 
			
		||||
		MsgType:       msgtype,
 | 
			
		||||
		Format:        "org.matrix.custom.html",
 | 
			
		||||
		FormattedBody: htmlText,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								vendor/github.com/matterbridge/gomatrix/filter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/github.com/matterbridge/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"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								vendor/github.com/matterbridge/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/matterbridge/gomatrix/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
type ReqRegister struct {
 | 
			
		||||
	Username                 string      `json:"username,omitempty"`
 | 
			
		||||
	BindEmail                bool        `json:"bind_email,omitempty"`
 | 
			
		||||
	Password                 string      `json:"password,omitempty"`
 | 
			
		||||
	DeviceID                 string      `json:"device_id,omitempty"`
 | 
			
		||||
	InitialDeviceDisplayName string      `json:"initial_device_display_name"`
 | 
			
		||||
	Auth                     interface{} `json:"auth,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
 | 
			
		||||
type ReqLogin struct {
 | 
			
		||||
	Type                     string `json:"type"`
 | 
			
		||||
	Password                 string `json:"password,omitempty"`
 | 
			
		||||
	Medium                   string `json:"medium,omitempty"`
 | 
			
		||||
	User                     string `json:"user,omitempty"`
 | 
			
		||||
	Address                  string `json:"address,omitempty"`
 | 
			
		||||
	Token                    string `json:"token,omitempty"`
 | 
			
		||||
	DeviceID                 string `json:"device_id,omitempty"`
 | 
			
		||||
	InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
type ReqCreateRoom struct {
 | 
			
		||||
	Visibility      string                 `json:"visibility,omitempty"`
 | 
			
		||||
	RoomAliasName   string                 `json:"room_alias_name,omitempty"`
 | 
			
		||||
	Name            string                 `json:"name,omitempty"`
 | 
			
		||||
	Topic           string                 `json:"topic,omitempty"`
 | 
			
		||||
	Invite          []string               `json:"invite,omitempty"`
 | 
			
		||||
	Invite3PID      []ReqInvite3PID        `json:"invite_3pid,omitempty"`
 | 
			
		||||
	CreationContent map[string]interface{} `json:"creation_content,omitempty"`
 | 
			
		||||
	InitialState    []Event                `json:"initial_state,omitempty"`
 | 
			
		||||
	Preset          string                 `json:"preset,omitempty"`
 | 
			
		||||
	IsDirect        bool                   `json:"is_direct,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
 | 
			
		||||
type ReqRedact struct {
 | 
			
		||||
	Reason string `json:"reason,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57
 | 
			
		||||
// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
type ReqInvite3PID struct {
 | 
			
		||||
	IDServer string `json:"id_server"`
 | 
			
		||||
	Medium   string `json:"medium"`
 | 
			
		||||
	Address  string `json:"address"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
 | 
			
		||||
type ReqInviteUser struct {
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
 | 
			
		||||
type ReqKickUser struct {
 | 
			
		||||
	Reason string `json:"reason,omitempty"`
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
 | 
			
		||||
type ReqBanUser struct {
 | 
			
		||||
	Reason string `json:"reason,omitempty"`
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
type ReqUnbanUser struct {
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
type ReqTyping struct {
 | 
			
		||||
	Typing  bool  `json:"typing"`
 | 
			
		||||
	Timeout int64 `json:"timeout"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										176
									
								
								vendor/github.com/matterbridge/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								vendor/github.com/matterbridge/gomatrix/responses.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
 | 
			
		||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
 | 
			
		||||
type RespError struct {
 | 
			
		||||
	ErrCode string `json:"errcode"`
 | 
			
		||||
	Err     string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns the errcode and error message.
 | 
			
		||||
func (e RespError) Error() string {
 | 
			
		||||
	return e.ErrCode + ": " + e.Err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
 | 
			
		||||
type RespCreateFilter struct {
 | 
			
		||||
	FilterID string `json:"filter_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
 | 
			
		||||
type RespVersions struct {
 | 
			
		||||
	Versions []string `json:"versions"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join
 | 
			
		||||
type RespJoinRoom struct {
 | 
			
		||||
	RoomID string `json:"room_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
 | 
			
		||||
type RespLeaveRoom struct{}
 | 
			
		||||
 | 
			
		||||
// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
 | 
			
		||||
type RespForgetRoom struct{}
 | 
			
		||||
 | 
			
		||||
// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
 | 
			
		||||
type RespInviteUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
 | 
			
		||||
type RespKickUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
 | 
			
		||||
type RespBanUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
 | 
			
		||||
type RespUnbanUser struct{}
 | 
			
		||||
 | 
			
		||||
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
 | 
			
		||||
type RespTyping struct{}
 | 
			
		||||
 | 
			
		||||
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
type RespJoinedRooms struct {
 | 
			
		||||
	JoinedRooms []string `json:"joined_rooms"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespJoinedMembers is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
 | 
			
		||||
type RespJoinedMembers struct {
 | 
			
		||||
	Joined map[string]struct {
 | 
			
		||||
		DisplayName *string `json:"display_name"`
 | 
			
		||||
		AvatarURL   *string `json:"avatar_url"`
 | 
			
		||||
	} `json:"joined"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
 | 
			
		||||
type RespMessages struct {
 | 
			
		||||
	Start string  `json:"start"`
 | 
			
		||||
	Chunk []Event `json:"chunk"`
 | 
			
		||||
	End   string  `json:"end"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
 | 
			
		||||
type RespSendEvent struct {
 | 
			
		||||
	EventID string `json:"event_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
 | 
			
		||||
type RespMediaUpload struct {
 | 
			
		||||
	ContentURI string `json:"content_uri"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
 | 
			
		||||
type RespUserInteractive struct {
 | 
			
		||||
	Flows []struct {
 | 
			
		||||
		Stages []string `json:"stages"`
 | 
			
		||||
	} `json:"flows"`
 | 
			
		||||
	Params    map[string]interface{} `json:"params"`
 | 
			
		||||
	Session   string                 `json:"string"`
 | 
			
		||||
	Completed []string               `json:"completed"`
 | 
			
		||||
	ErrCode   string                 `json:"errcode"`
 | 
			
		||||
	Error     string                 `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
 | 
			
		||||
func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
 | 
			
		||||
	for _, f := range r.Flows {
 | 
			
		||||
		if len(f.Stages) == 1 && f.Stages[0] == stageName {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
 | 
			
		||||
type RespUserDisplayName struct {
 | 
			
		||||
	DisplayName string `json:"displayname"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
 | 
			
		||||
type RespRegister struct {
 | 
			
		||||
	AccessToken  string `json:"access_token"`
 | 
			
		||||
	DeviceID     string `json:"device_id"`
 | 
			
		||||
	HomeServer   string `json:"home_server"`
 | 
			
		||||
	RefreshToken string `json:"refresh_token"`
 | 
			
		||||
	UserID       string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
 | 
			
		||||
type RespLogin struct {
 | 
			
		||||
	AccessToken string `json:"access_token"`
 | 
			
		||||
	DeviceID    string `json:"device_id"`
 | 
			
		||||
	HomeServer  string `json:"home_server"`
 | 
			
		||||
	UserID      string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
 | 
			
		||||
type RespLogout struct{}
 | 
			
		||||
 | 
			
		||||
// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
 | 
			
		||||
type RespCreateRoom struct {
 | 
			
		||||
	RoomID string `json:"room_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
 | 
			
		||||
type RespSync struct {
 | 
			
		||||
	NextBatch   string `json:"next_batch"`
 | 
			
		||||
	AccountData struct {
 | 
			
		||||
		Events []Event `json:"events"`
 | 
			
		||||
	} `json:"account_data"`
 | 
			
		||||
	Presence struct {
 | 
			
		||||
		Events []Event `json:"events"`
 | 
			
		||||
	} `json:"presence"`
 | 
			
		||||
	Rooms struct {
 | 
			
		||||
		Leave map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event `json:"events"`
 | 
			
		||||
			} `json:"state"`
 | 
			
		||||
			Timeline struct {
 | 
			
		||||
				Events    []Event `json:"events"`
 | 
			
		||||
				Limited   bool    `json:"limited"`
 | 
			
		||||
				PrevBatch string  `json:"prev_batch"`
 | 
			
		||||
			} `json:"timeline"`
 | 
			
		||||
		} `json:"leave"`
 | 
			
		||||
		Join map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event `json:"events"`
 | 
			
		||||
			} `json:"state"`
 | 
			
		||||
			Timeline struct {
 | 
			
		||||
				Events    []Event `json:"events"`
 | 
			
		||||
				Limited   bool    `json:"limited"`
 | 
			
		||||
				PrevBatch string  `json:"prev_batch"`
 | 
			
		||||
			} `json:"timeline"`
 | 
			
		||||
		} `json:"join"`
 | 
			
		||||
		Invite map[string]struct {
 | 
			
		||||
			State struct {
 | 
			
		||||
				Events []Event
 | 
			
		||||
			} `json:"invite_state"`
 | 
			
		||||
		} `json:"invite"`
 | 
			
		||||
	} `json:"rooms"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RespTurnServer struct {
 | 
			
		||||
	Username string   `json:"username"`
 | 
			
		||||
	Password string   `json:"password"`
 | 
			
		||||
	TTL      int      `json:"ttl"`
 | 
			
		||||
	URIs     []string `json:"uris"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								vendor/github.com/matterbridge/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/matterbridge/gomatrix/room.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// Room represents a single Matrix room.
 | 
			
		||||
type Room struct {
 | 
			
		||||
	ID    string
 | 
			
		||||
	State map[string]map[string]*Event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateState updates the room's current state with the given Event. This will clobber events based
 | 
			
		||||
// on the type/state_key combination.
 | 
			
		||||
func (room Room) UpdateState(event *Event) {
 | 
			
		||||
	_, exists := room.State[event.Type]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		room.State[event.Type] = make(map[string]*Event)
 | 
			
		||||
	}
 | 
			
		||||
	room.State[event.Type][*event.StateKey] = event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
 | 
			
		||||
func (room Room) GetStateEvent(eventType string, stateKey string) *Event {
 | 
			
		||||
	stateEventMap, _ := room.State[eventType]
 | 
			
		||||
	event, _ := stateEventMap[stateKey]
 | 
			
		||||
	return event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMembershipState returns the membership state of the given user ID in this room. If there is
 | 
			
		||||
// no entry for this member, 'leave' is returned for consistency with left users.
 | 
			
		||||
func (room Room) GetMembershipState(userID string) string {
 | 
			
		||||
	state := "leave"
 | 
			
		||||
	event := room.GetStateEvent("m.room.member", userID)
 | 
			
		||||
	if event != nil {
 | 
			
		||||
		membershipState, found := event.Content["membership"]
 | 
			
		||||
		if found {
 | 
			
		||||
			mState, isString := membershipState.(string)
 | 
			
		||||
			if isString {
 | 
			
		||||
				state = mState
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRoom creates a new Room with the given ID
 | 
			
		||||
func NewRoom(roomID string) *Room {
 | 
			
		||||
	// Init the State map and return a pointer to the Room
 | 
			
		||||
	return &Room{
 | 
			
		||||
		ID:    roomID,
 | 
			
		||||
		State: make(map[string]map[string]*Event),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/matterbridge/gomatrix/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/matterbridge/gomatrix/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
// Storer is an interface which must be satisfied to store client data.
 | 
			
		||||
//
 | 
			
		||||
// You can either write a struct which persists this data to disk, or you can use the
 | 
			
		||||
// provided "InMemoryStore" which just keeps data around in-memory which is lost on
 | 
			
		||||
// restarts.
 | 
			
		||||
type Storer interface {
 | 
			
		||||
	SaveFilterID(userID, filterID string)
 | 
			
		||||
	LoadFilterID(userID string) string
 | 
			
		||||
	SaveNextBatch(userID, nextBatchToken string)
 | 
			
		||||
	LoadNextBatch(userID string) string
 | 
			
		||||
	SaveRoom(room *Room)
 | 
			
		||||
	LoadRoom(roomID string) *Room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InMemoryStore implements the Storer interface.
 | 
			
		||||
//
 | 
			
		||||
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
 | 
			
		||||
// or next batch tokens on any goroutine other than the syncing goroutine: the one
 | 
			
		||||
// which called Client.Sync().
 | 
			
		||||
type InMemoryStore struct {
 | 
			
		||||
	Filters   map[string]string
 | 
			
		||||
	NextBatch map[string]string
 | 
			
		||||
	Rooms     map[string]*Room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveFilterID to memory.
 | 
			
		||||
func (s *InMemoryStore) SaveFilterID(userID, filterID string) {
 | 
			
		||||
	s.Filters[userID] = filterID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadFilterID from memory.
 | 
			
		||||
func (s *InMemoryStore) LoadFilterID(userID string) string {
 | 
			
		||||
	return s.Filters[userID]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveNextBatch to memory.
 | 
			
		||||
func (s *InMemoryStore) SaveNextBatch(userID, nextBatchToken string) {
 | 
			
		||||
	s.NextBatch[userID] = nextBatchToken
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadNextBatch from memory.
 | 
			
		||||
func (s *InMemoryStore) LoadNextBatch(userID string) string {
 | 
			
		||||
	return s.NextBatch[userID]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveRoom to memory.
 | 
			
		||||
func (s *InMemoryStore) SaveRoom(room *Room) {
 | 
			
		||||
	s.Rooms[room.ID] = room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadRoom from memory.
 | 
			
		||||
func (s *InMemoryStore) LoadRoom(roomID string) *Room {
 | 
			
		||||
	return s.Rooms[roomID]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInMemoryStore constructs a new InMemoryStore.
 | 
			
		||||
func NewInMemoryStore() *InMemoryStore {
 | 
			
		||||
	return &InMemoryStore{
 | 
			
		||||
		Filters:   make(map[string]string),
 | 
			
		||||
		NextBatch: make(map[string]string),
 | 
			
		||||
		Rooms:     make(map[string]*Room),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								vendor/github.com/matterbridge/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								vendor/github.com/matterbridge/gomatrix/sync.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Syncer represents an interface that must be satisfied in order to do /sync requests on a client.
 | 
			
		||||
type Syncer interface {
 | 
			
		||||
	// Process the /sync response. The since parameter is the since= value that was used to produce the response.
 | 
			
		||||
	// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped
 | 
			
		||||
	// permanently.
 | 
			
		||||
	ProcessResponse(resp *RespSync, since string) error
 | 
			
		||||
	// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
 | 
			
		||||
	OnFailedSync(res *RespSync, err error) (time.Duration, error)
 | 
			
		||||
	// GetFilterJSON for the given user ID. NOT the filter ID.
 | 
			
		||||
	GetFilterJSON(userID string) json.RawMessage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
 | 
			
		||||
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
 | 
			
		||||
// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
 | 
			
		||||
type DefaultSyncer struct {
 | 
			
		||||
	UserID    string
 | 
			
		||||
	Store     Storer
 | 
			
		||||
	listeners map[string][]OnEventListener // event type to listeners array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
 | 
			
		||||
type OnEventListener func(*Event)
 | 
			
		||||
 | 
			
		||||
// NewDefaultSyncer returns an instantiated DefaultSyncer
 | 
			
		||||
func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer {
 | 
			
		||||
	return &DefaultSyncer{
 | 
			
		||||
		UserID:    userID,
 | 
			
		||||
		Store:     store,
 | 
			
		||||
		listeners: make(map[string][]OnEventListener),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
 | 
			
		||||
// unrepeating events. Returns a fatal error if a listener panics.
 | 
			
		||||
func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
 | 
			
		||||
	if !s.shouldProcessResponse(res, since) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack())
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Join {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.State.Events {
 | 
			
		||||
			event.RoomID = roomID
 | 
			
		||||
			room.UpdateState(&event)
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
		for _, event := range roomData.Timeline.Events {
 | 
			
		||||
			event.RoomID = roomID
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Invite {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.State.Events {
 | 
			
		||||
			event.RoomID = roomID
 | 
			
		||||
			room.UpdateState(&event)
 | 
			
		||||
			s.notifyListeners(&event)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for roomID, roomData := range res.Rooms.Leave {
 | 
			
		||||
		room := s.getOrCreateRoom(roomID)
 | 
			
		||||
		for _, event := range roomData.Timeline.Events {
 | 
			
		||||
			if event.StateKey != nil {
 | 
			
		||||
				event.RoomID = roomID
 | 
			
		||||
				room.UpdateState(&event)
 | 
			
		||||
				s.notifyListeners(&event)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnEventType allows callers to be notified when there are new events for the given event type.
 | 
			
		||||
// There are no duplicate checks.
 | 
			
		||||
func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) {
 | 
			
		||||
	_, exists := s.listeners[eventType]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		s.listeners[eventType] = []OnEventListener{}
 | 
			
		||||
	}
 | 
			
		||||
	s.listeners[eventType] = append(s.listeners[eventType], callback)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
 | 
			
		||||
// stuff that shouldn't be processed.
 | 
			
		||||
func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool {
 | 
			
		||||
	if since == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	// This is a horrible hack because /sync will return the most recent messages for a room
 | 
			
		||||
	// as soon as you /join it. We do NOT want to process those events in that particular room
 | 
			
		||||
	// because they may have already been processed (if you toggle the bot in/out of the room).
 | 
			
		||||
	//
 | 
			
		||||
	// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
 | 
			
		||||
	// exists and is "join" and then discard processing that room entirely if so.
 | 
			
		||||
	// TODO: We probably want to process messages from after the last join event in the timeline.
 | 
			
		||||
	for roomID, roomData := range resp.Rooms.Join {
 | 
			
		||||
		for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
 | 
			
		||||
			e := roomData.Timeline.Events[i]
 | 
			
		||||
			if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
 | 
			
		||||
				m := e.Content["membership"]
 | 
			
		||||
				mship, ok := m.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if mship == "join" {
 | 
			
		||||
					_, ok := resp.Rooms.Join[roomID]
 | 
			
		||||
					if !ok {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					delete(resp.Rooms.Join, roomID)   // don't re-process messages
 | 
			
		||||
					delete(resp.Rooms.Invite, roomID) // don't re-process invites
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse()
 | 
			
		||||
func (s *DefaultSyncer) getOrCreateRoom(roomID string) *Room {
 | 
			
		||||
	room := s.Store.LoadRoom(roomID)
 | 
			
		||||
	if room == nil { // create a new Room
 | 
			
		||||
		room = NewRoom(roomID)
 | 
			
		||||
		s.Store.SaveRoom(room)
 | 
			
		||||
	}
 | 
			
		||||
	return room
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DefaultSyncer) notifyListeners(event *Event) {
 | 
			
		||||
	listeners, exists := s.listeners[event.Type]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, fn := range listeners {
 | 
			
		||||
		fn(event)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
 | 
			
		||||
func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
 | 
			
		||||
	return 10 * time.Second, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFilterJSON returns a filter with a timeline limit of 50.
 | 
			
		||||
func (s *DefaultSyncer) GetFilterJSON(userID string) json.RawMessage {
 | 
			
		||||
	return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								vendor/github.com/matterbridge/gomatrix/userids.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								vendor/github.com/matterbridge/gomatrix/userids.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
package gomatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const lowerhex = "0123456789abcdef"
 | 
			
		||||
 | 
			
		||||
// encode the given byte using quoted-printable encoding (e.g "=2f")
 | 
			
		||||
// and writes it to the buffer
 | 
			
		||||
// See https://golang.org/src/mime/quotedprintable/writer.go
 | 
			
		||||
func encode(buf *bytes.Buffer, b byte) {
 | 
			
		||||
	buf.WriteByte('=')
 | 
			
		||||
	buf.WriteByte(lowerhex[b>>4])
 | 
			
		||||
	buf.WriteByte(lowerhex[b&0x0f])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// escape the given alpha character and writes it to the buffer
 | 
			
		||||
func escape(buf *bytes.Buffer, b byte) {
 | 
			
		||||
	buf.WriteByte('_')
 | 
			
		||||
	if b == '_' {
 | 
			
		||||
		buf.WriteByte('_') // another _
 | 
			
		||||
	} else {
 | 
			
		||||
		buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shouldEncode(b byte) bool {
 | 
			
		||||
	return b != '-' && b != '.' && b != '_' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shouldEscape(b byte) bool {
 | 
			
		||||
	return (b >= 'A' && b <= 'Z') || b == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidByte(b byte) bool {
 | 
			
		||||
	return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidEscapedChar(b byte) bool {
 | 
			
		||||
	return b == '_' || (b >= 'a' && b <= 'z')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form.
 | 
			
		||||
// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
 | 
			
		||||
//
 | 
			
		||||
// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z
 | 
			
		||||
// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges
 | 
			
		||||
// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs)
 | 
			
		||||
// and converted to lower-case hex with a leading "=". For example:
 | 
			
		||||
//   Alph@Bet_50up  => _alph=40_bet=5f50up
 | 
			
		||||
func EncodeUserLocalpart(str string) string {
 | 
			
		||||
	strBytes := []byte(str)
 | 
			
		||||
	var outputBuffer bytes.Buffer
 | 
			
		||||
	for _, b := range strBytes {
 | 
			
		||||
		if shouldEncode(b) {
 | 
			
		||||
			encode(&outputBuffer, b)
 | 
			
		||||
		} else if shouldEscape(b) {
 | 
			
		||||
			escape(&outputBuffer, b)
 | 
			
		||||
		} else {
 | 
			
		||||
			outputBuffer.WriteByte(b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return outputBuffer.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeUserLocalpart decodes the given string back into the original input string.
 | 
			
		||||
// Returns an error if the given string is not a valid user ID localpart encoding.
 | 
			
		||||
// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
 | 
			
		||||
//
 | 
			
		||||
// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For
 | 
			
		||||
// example:
 | 
			
		||||
//  _alph=40_bet=5f50up  =>  Alph@Bet_50up
 | 
			
		||||
// Returns an error if the input string contains characters outside the
 | 
			
		||||
// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has
 | 
			
		||||
// an invalid _ escaped byte (e.g. "_5").
 | 
			
		||||
func DecodeUserLocalpart(str string) (string, error) {
 | 
			
		||||
	strBytes := []byte(str)
 | 
			
		||||
	var outputBuffer bytes.Buffer
 | 
			
		||||
	for i := 0; i < len(strBytes); i++ {
 | 
			
		||||
		b := strBytes[i]
 | 
			
		||||
		if !isValidByte(b) {
 | 
			
		||||
			return "", fmt.Errorf("Byte pos %d: Invalid byte", i)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _
 | 
			
		||||
			if i+1 >= len(strBytes) {
 | 
			
		||||
				return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i)
 | 
			
		||||
			}
 | 
			
		||||
			if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping
 | 
			
		||||
				return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i)
 | 
			
		||||
			}
 | 
			
		||||
			if strBytes[i+1] == '_' {
 | 
			
		||||
				outputBuffer.WriteByte('_')
 | 
			
		||||
			} else {
 | 
			
		||||
				outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z
 | 
			
		||||
			}
 | 
			
		||||
			i++ // skip next byte since we just handled it
 | 
			
		||||
		} else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8
 | 
			
		||||
			if i+2 >= len(strBytes) {
 | 
			
		||||
				return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i)
 | 
			
		||||
			}
 | 
			
		||||
			dst := make([]byte, 1)
 | 
			
		||||
			_, err := hex.Decode(dst, strBytes[i+1:i+3])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
			outputBuffer.WriteByte(dst[0])
 | 
			
		||||
			i += 2 // skip next 2 bytes since we just handled it
 | 
			
		||||
		} else { // pass through
 | 
			
		||||
			outputBuffer.WriteByte(b)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return outputBuffer.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractUserLocalpart extracts the localpart portion of a user ID.
 | 
			
		||||
// See http://matrix.org/docs/spec/intro.html#user-identifiers
 | 
			
		||||
func ExtractUserLocalpart(userID string) (string, error) {
 | 
			
		||||
	if len(userID) == 0 || userID[0] != '@' {
 | 
			
		||||
		return "", fmt.Errorf("%s is not a valid user id", userID)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimPrefix(
 | 
			
		||||
		strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ]
 | 
			
		||||
		"@", // remove "@" prefix
 | 
			
		||||
	), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/matterbridge/slack/websocket_utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/matterbridge/slack/websocket_utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,20 +0,0 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var portMapping = map[string]string{"ws": "80", "wss": "443"}
 | 
			
		||||
 | 
			
		||||
func websocketizeURLPort(orig string) (string, error) {
 | 
			
		||||
	urlObj, err := url.ParseRequestURI(orig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = net.SplitHostPort(urlObj.Host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return urlObj.Scheme + "://" + urlObj.Host + ":" + portMapping[urlObj.Scheme] + urlObj.Path, nil
 | 
			
		||||
	}
 | 
			
		||||
	return orig, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -25,6 +25,7 @@ type AttachmentAction struct {
 | 
			
		||||
	SelectedOptions []AttachmentActionOption      `json:"selected_options,omitempty"` // Optional. The first element of this array will be set as the pre-selected option for this menu.
 | 
			
		||||
	OptionGroups    []AttachmentActionOptionGroup `json:"option_groups,omitempty"`    // Optional.
 | 
			
		||||
	Confirm         *ConfirmationField            `json:"confirm,omitempty"`          // Optional.
 | 
			
		||||
	URL             string                        `json:"url,omitempty"`              // Optional.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttachmentActionOption the individual option to appear in action menu.
 | 
			
		||||
@@ -48,6 +49,9 @@ type AttachmentActionCallback struct {
 | 
			
		||||
	Channel    Channel            `json:"channel"`
 | 
			
		||||
	User       User               `json:"user"`
 | 
			
		||||
 | 
			
		||||
	Name  string `json:"name"`
 | 
			
		||||
	Value string `json:"value"`
 | 
			
		||||
 | 
			
		||||
	OriginalMessage Message `json:"original_message"`
 | 
			
		||||
 | 
			
		||||
	ActionTs     string `json:"action_ts"`
 | 
			
		||||
@@ -38,51 +38,51 @@ func channelRequest(ctx context.Context, path string, values url.Values, debug b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveChannel archives the given channel
 | 
			
		||||
func (api *Client) ArchiveChannel(channel string) error {
 | 
			
		||||
	return api.ArchiveChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.archive
 | 
			
		||||
func (api *Client) ArchiveChannel(channelID string) error {
 | 
			
		||||
	return api.ArchiveChannelContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveChannelContext archives the given channel with a custom context
 | 
			
		||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channel string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.archive
 | 
			
		||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.archive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveChannel unarchives the given channel
 | 
			
		||||
func (api *Client) UnarchiveChannel(channel string) error {
 | 
			
		||||
	return api.UnarchiveChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.unarchive
 | 
			
		||||
func (api *Client) UnarchiveChannel(channelID string) error {
 | 
			
		||||
	return api.UnarchiveChannelContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveChannelContext unarchives the given channel with a custom context
 | 
			
		||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channel string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.unarchive
 | 
			
		||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.unarchive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateChannel creates a channel with the given name and returns a *Channel
 | 
			
		||||
func (api *Client) CreateChannel(channel string) (*Channel, error) {
 | 
			
		||||
	return api.CreateChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.create
 | 
			
		||||
func (api *Client) CreateChannel(channelName string) (*Channel, error) {
 | 
			
		||||
	return api.CreateChannelContext(context.Background(), channelName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateChannelContext creates a channel with the given name and returns a *Channel with a custom context
 | 
			
		||||
func (api *Client) CreateChannelContext(ctx context.Context, channel string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.create
 | 
			
		||||
func (api *Client) CreateChannelContext(ctx context.Context, channelName string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
		"name":  {channelName},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.create", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -92,15 +92,17 @@ func (api *Client) CreateChannelContext(ctx context.Context, channel string) (*C
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelHistory retrieves the channel history
 | 
			
		||||
func (api *Client) GetChannelHistory(channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	return api.GetChannelHistoryContext(context.Background(), channel, params)
 | 
			
		||||
// see https://api.slack.com/methods/channels.history
 | 
			
		||||
func (api *Client) GetChannelHistory(channelID string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	return api.GetChannelHistoryContext(context.Background(), channelID, params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelHistoryContext retrieves the channel history with a custom context
 | 
			
		||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.history
 | 
			
		||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Latest != DEFAULT_HISTORY_LATEST {
 | 
			
		||||
		values.Add("latest", params.Latest)
 | 
			
		||||
@@ -133,15 +135,17 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelInfo retrieves the given channel
 | 
			
		||||
func (api *Client) GetChannelInfo(channel string) (*Channel, error) {
 | 
			
		||||
	return api.GetChannelInfoContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.info
 | 
			
		||||
func (api *Client) GetChannelInfo(channelID string) (*Channel, error) {
 | 
			
		||||
	return api.GetChannelInfoContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelInfoContext retrieves the given channel with a custom context
 | 
			
		||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channel string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.info
 | 
			
		||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -151,15 +155,17 @@ func (api *Client) GetChannelInfoContext(ctx context.Context, channel string) (*
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToChannel invites a user to a given channel and returns a *Channel
 | 
			
		||||
func (api *Client) InviteUserToChannel(channel, user string) (*Channel, error) {
 | 
			
		||||
	return api.InviteUserToChannelContext(context.Background(), channel, user)
 | 
			
		||||
// see https://api.slack.com/methods/channels.invite
 | 
			
		||||
func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error) {
 | 
			
		||||
	return api.InviteUserToChannelContext(context.Background(), channelID, user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToChannelCustom invites a user to a given channel and returns a *Channel with a custom context
 | 
			
		||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channel, user string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.invite
 | 
			
		||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.invite", values, api.debug)
 | 
			
		||||
@@ -170,15 +176,17 @@ func (api *Client) InviteUserToChannelContext(ctx context.Context, channel, user
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinChannel joins the currently authenticated user to a channel
 | 
			
		||||
func (api *Client) JoinChannel(channel string) (*Channel, error) {
 | 
			
		||||
	return api.JoinChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.join
 | 
			
		||||
func (api *Client) JoinChannel(channelName string) (*Channel, error) {
 | 
			
		||||
	return api.JoinChannelContext(context.Background(), channelName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinChannelContext joins the currently authenticated user to a channel with a custom context
 | 
			
		||||
func (api *Client) JoinChannelContext(ctx context.Context, channel string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.join
 | 
			
		||||
func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
		"name":  {channelName},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.join", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -188,15 +196,17 @@ func (api *Client) JoinChannelContext(ctx context.Context, channel string) (*Cha
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveChannel makes the authenticated user leave the given channel
 | 
			
		||||
func (api *Client) LeaveChannel(channel string) (bool, error) {
 | 
			
		||||
	return api.LeaveChannelContext(context.Background(), channel)
 | 
			
		||||
// see https://api.slack.com/methods/channels.leave
 | 
			
		||||
func (api *Client) LeaveChannel(channelID string) (bool, error) {
 | 
			
		||||
	return api.LeaveChannelContext(context.Background(), channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveChannelContext makes the authenticated user leave the given channel with a custom context
 | 
			
		||||
func (api *Client) LeaveChannelContext(ctx context.Context, channel string) (bool, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.leave
 | 
			
		||||
func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -209,30 +219,31 @@ func (api *Client) LeaveChannelContext(ctx context.Context, channel string) (boo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromChannel kicks a user from a given channel
 | 
			
		||||
func (api *Client) KickUserFromChannel(channel, user string) error {
 | 
			
		||||
	return api.KickUserFromChannelContext(context.Background(), channel, user)
 | 
			
		||||
// see https://api.slack.com/methods/channels.kick
 | 
			
		||||
func (api *Client) KickUserFromChannel(channelID, user string) error {
 | 
			
		||||
	return api.KickUserFromChannelContext(context.Background(), channelID, user)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromChannelContext kicks a user from a given channel with a custom context
 | 
			
		||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channel, user string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.kick
 | 
			
		||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannels retrieves all the channels
 | 
			
		||||
// see https://api.slack.com/methods/channels.list
 | 
			
		||||
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
 | 
			
		||||
	return api.GetChannelsContext(context.Background(), excludeArchived)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelsContext retrieves all the channels with a custom context
 | 
			
		||||
// see https://api.slack.com/methods/channels.list
 | 
			
		||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) ([]Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
@@ -252,35 +263,36 @@ func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool)
 | 
			
		||||
// timer before making the call. In this way, any further updates needed during the timeout will not generate extra calls
 | 
			
		||||
// (just one per channel). This is useful for when reading scroll-back history, or following a busy live channel. A
 | 
			
		||||
// timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout.
 | 
			
		||||
func (api *Client) SetChannelReadMark(channel, ts string) error {
 | 
			
		||||
	return api.SetChannelReadMarkContext(context.Background(), channel, ts)
 | 
			
		||||
// see https://api.slack.com/methods/channels.mark
 | 
			
		||||
func (api *Client) SetChannelReadMark(channelID, ts string) error {
 | 
			
		||||
	return api.SetChannelReadMarkContext(context.Background(), channelID, ts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelReadMarkContext sets the read mark of a given channel to a specific point with a custom context
 | 
			
		||||
// For more details see SetChannelReadMark documentation
 | 
			
		||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channel, ts string) error {
 | 
			
		||||
// see https://api.slack.com/methods/channels.mark
 | 
			
		||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest(ctx, "channels.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameChannel renames a given channel
 | 
			
		||||
func (api *Client) RenameChannel(channel, name string) (*Channel, error) {
 | 
			
		||||
	return api.RenameChannelContext(context.Background(), channel, name)
 | 
			
		||||
// see https://api.slack.com/methods/channels.rename
 | 
			
		||||
func (api *Client) RenameChannel(channelID, name string) (*Channel, error) {
 | 
			
		||||
	return api.RenameChannelContext(context.Background(), channelID, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameChannelContext renames a given channel with a custom context
 | 
			
		||||
func (api *Client) RenameChannelContext(ctx context.Context, channel, name string) (*Channel, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.rename
 | 
			
		||||
func (api *Client) RenameChannelContext(ctx context.Context, channelID, name string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"name":    {name},
 | 
			
		||||
	}
 | 
			
		||||
	// XXX: the created entry in this call returns a string instead of a number
 | 
			
		||||
@@ -293,15 +305,17 @@ func (api *Client) RenameChannelContext(ctx context.Context, channel, name strin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelPurpose sets the channel purpose and returns the purpose that was successfully set
 | 
			
		||||
func (api *Client) SetChannelPurpose(channel, purpose string) (string, error) {
 | 
			
		||||
	return api.SetChannelPurposeContext(context.Background(), channel, purpose)
 | 
			
		||||
// see https://api.slack.com/methods/channels.setPurpose
 | 
			
		||||
func (api *Client) SetChannelPurpose(channelID, purpose string) (string, error) {
 | 
			
		||||
	return api.SetChannelPurposeContext(context.Background(), channelID, purpose)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelPurposeContext sets the channel purpose and returns the purpose that was successfully set with a custom context
 | 
			
		||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channel, purpose string) (string, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.setPurpose
 | 
			
		||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purpose string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"purpose": {purpose},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.setPurpose", values, api.debug)
 | 
			
		||||
@@ -312,15 +326,17 @@ func (api *Client) SetChannelPurposeContext(ctx context.Context, channel, purpos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelTopic sets the channel topic and returns the topic that was successfully set
 | 
			
		||||
func (api *Client) SetChannelTopic(channel, topic string) (string, error) {
 | 
			
		||||
	return api.SetChannelTopicContext(context.Background(), channel, topic)
 | 
			
		||||
// see https://api.slack.com/methods/channels.setTopic
 | 
			
		||||
func (api *Client) SetChannelTopic(channelID, topic string) (string, error) {
 | 
			
		||||
	return api.SetChannelTopicContext(context.Background(), channelID, topic)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelTopicContext sets the channel topic and returns the topic that was successfully set with a custom context
 | 
			
		||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channel, topic string) (string, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.setTopic
 | 
			
		||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"channel": {channelID},
 | 
			
		||||
		"topic":   {topic},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.setTopic", values, api.debug)
 | 
			
		||||
@@ -331,15 +347,17 @@ func (api *Client) SetChannelTopicContext(ctx context.Context, channel, topic st
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelReplies gets an entire thread (a message plus all the messages in reply to it).
 | 
			
		||||
func (api *Client) GetChannelReplies(channel, thread_ts string) ([]Message, error) {
 | 
			
		||||
	return api.GetChannelRepliesContext(context.Background(), channel, thread_ts)
 | 
			
		||||
// see https://api.slack.com/methods/channels.replies
 | 
			
		||||
func (api *Client) GetChannelReplies(channelID, thread_ts string) ([]Message, error) {
 | 
			
		||||
	return api.GetChannelRepliesContext(context.Background(), channelID, thread_ts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelRepliesContext gets an entire thread (a message plus all the messages in reply to it) with a custom context
 | 
			
		||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channel, thread_ts string) ([]Message, error) {
 | 
			
		||||
// see https://api.slack.com/methods/channels.replies
 | 
			
		||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thread_ts string) ([]Message, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":     {api.config.token},
 | 
			
		||||
		"channel":   {channel},
 | 
			
		||||
		"channel":   {channelID},
 | 
			
		||||
		"thread_ts": {thread_ts},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest(ctx, "channels.replies", values, api.debug)
 | 
			
		||||
							
								
								
									
										62
									
								
								vendor/github.com/matterbridge/slack/chat.go → vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/matterbridge/slack/chat.go → vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,6 +11,7 @@ import (
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_MESSAGE_USERNAME         = ""
 | 
			
		||||
	DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
 | 
			
		||||
	DEFAULT_MESSAGE_REPLY_BROADCAST  = false
 | 
			
		||||
	DEFAULT_MESSAGE_ASUSER           = false
 | 
			
		||||
	DEFAULT_MESSAGE_PARSE            = ""
 | 
			
		||||
	DEFAULT_MESSAGE_LINK_NAMES       = 0
 | 
			
		||||
@@ -36,6 +37,7 @@ type PostMessageParameters struct {
 | 
			
		||||
	AsUser          bool         `json:"as_user"`
 | 
			
		||||
	Parse           string       `json:"parse"`
 | 
			
		||||
	ThreadTimestamp string       `json:"thread_ts"`
 | 
			
		||||
	ReplyBroadcast  bool         `json:"reply_broadcast"`
 | 
			
		||||
	LinkNames       int          `json:"link_names"`
 | 
			
		||||
	Attachments     []Attachment `json:"attachments"`
 | 
			
		||||
	UnfurlLinks     bool         `json:"unfurl_links"`
 | 
			
		||||
@@ -44,12 +46,17 @@ type PostMessageParameters struct {
 | 
			
		||||
	IconEmoji       string       `json:"icon_emoji"`
 | 
			
		||||
	Markdown        bool         `json:"mrkdwn,omitempty"`
 | 
			
		||||
	EscapeText      bool         `json:"escape_text"`
 | 
			
		||||
 | 
			
		||||
	// chat.postEphemeral support
 | 
			
		||||
	Channel string `json:"channel"`
 | 
			
		||||
	User    string `json:"user"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
 | 
			
		||||
func NewPostMessageParameters() PostMessageParameters {
 | 
			
		||||
	return PostMessageParameters{
 | 
			
		||||
		Username:    DEFAULT_MESSAGE_USERNAME,
 | 
			
		||||
		User:        DEFAULT_MESSAGE_USERNAME,
 | 
			
		||||
		AsUser:      DEFAULT_MESSAGE_ASUSER,
 | 
			
		||||
		Parse:       DEFAULT_MESSAGE_PARSE,
 | 
			
		||||
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
 | 
			
		||||
@@ -102,6 +109,37 @@ func (api *Client) PostMessageContext(ctx context.Context, channel, text string,
 | 
			
		||||
	return respChannel, respTimestamp, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostEphemeral sends an ephemeral message to a user in a channel.
 | 
			
		||||
// Message is escaped by default according to https://api.slack.com/docs/formatting
 | 
			
		||||
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
 | 
			
		||||
func (api *Client) PostEphemeral(channel, userID string, options ...MsgOption) (string, error) {
 | 
			
		||||
	options = append(options, MsgOptionPostEphemeral())
 | 
			
		||||
	return api.PostEphemeralContext(
 | 
			
		||||
		context.Background(),
 | 
			
		||||
		channel,
 | 
			
		||||
		userID,
 | 
			
		||||
		options...,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
 | 
			
		||||
// For more details, see PostEphemeral documentation
 | 
			
		||||
func (api *Client) PostEphemeralContext(ctx context.Context, channel, userID string, options ...MsgOption) (string, error) {
 | 
			
		||||
	path, values, err := ApplyMsgOptions(api.config.token, channel, options...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	values.Add("user", userID)
 | 
			
		||||
 | 
			
		||||
	response, err := chatRequest(ctx, path, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return response.Timestamp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateMessage updates a message in a channel
 | 
			
		||||
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
 | 
			
		||||
	return api.UpdateMessageContext(context.Background(), channel, timestamp, text)
 | 
			
		||||
@@ -171,9 +209,10 @@ func chatRequest(ctx context.Context, path string, values url.Values, debug bool
 | 
			
		||||
type sendMode string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	chatUpdate      sendMode = "chat.update"
 | 
			
		||||
	chatPostMessage sendMode = "chat.postMessage"
 | 
			
		||||
	chatDelete      sendMode = "chat.delete"
 | 
			
		||||
	chatUpdate        sendMode = "chat.update"
 | 
			
		||||
	chatPostMessage   sendMode = "chat.postMessage"
 | 
			
		||||
	chatDelete        sendMode = "chat.delete"
 | 
			
		||||
	chatPostEphemeral sendMode = "chat.postEphemeral"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type sendConfig struct {
 | 
			
		||||
@@ -193,6 +232,15 @@ func MsgOptionPost() MsgOption {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgOptionPostEphemeral posts an ephemeral message
 | 
			
		||||
func MsgOptionPostEphemeral() MsgOption {
 | 
			
		||||
	return func(config *sendConfig) error {
 | 
			
		||||
		config.mode = chatPostEphemeral
 | 
			
		||||
		config.values.Del("ts")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MsgOptionUpdate updates a message based on the timestamp.
 | 
			
		||||
func MsgOptionUpdate(timestamp string) MsgOption {
 | 
			
		||||
	return func(config *sendConfig) error {
 | 
			
		||||
@@ -279,6 +327,11 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
 | 
			
		||||
			config.values.Set("username", string(params.Username))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// chat.postEphemeral support
 | 
			
		||||
		if params.User != DEFAULT_MESSAGE_USERNAME {
 | 
			
		||||
			config.values.Set("user", params.User)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// never generates an error.
 | 
			
		||||
		MsgOptionAsUser(params.AsUser)(config)
 | 
			
		||||
 | 
			
		||||
@@ -314,6 +367,9 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
 | 
			
		||||
		if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
 | 
			
		||||
			config.values.Set("thread_ts", params.ThreadTimestamp)
 | 
			
		||||
		}
 | 
			
		||||
		if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
 | 
			
		||||
			config.values.Set("reply_broadcast", "true")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
							
								
								
									
										0
									
								
								vendor/github.com/matterbridge/slack/dnd.go → vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								vendor/github.com/matterbridge/slack/dnd.go → vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
								
								
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	channels, err := api.GetChannels(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, channel := range channels {
 | 
			
		||||
		fmt.Println(channel.Name)
 | 
			
		||||
		// channel is of type conversation & groupConversation
 | 
			
		||||
		// see all available methods in `conversation.go`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/nlopes/slack/examples/files/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/nlopes/slack/examples/files/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	params := slack.FileUploadParameters{
 | 
			
		||||
		Title: "Batman Example",
 | 
			
		||||
		//Filetype: "txt",
 | 
			
		||||
		File: "example.txt",
 | 
			
		||||
		//Content:  "Nan Nan Nan Nan Nan Nan Nan Nan Batman",
 | 
			
		||||
	}
 | 
			
		||||
	file, err := api.UploadFile(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Name: %s, URL: %s\n", file.Name, file.URL)
 | 
			
		||||
 | 
			
		||||
	err = api.DeleteFile(file.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("File %s deleted successfully.\n", file.Name)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/nlopes/slack/examples/groups/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/nlopes/slack/examples/groups/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	// If you set debugging, it will log all requests to the console
 | 
			
		||||
	// Useful when encountering issues
 | 
			
		||||
	// api.SetDebug(true)
 | 
			
		||||
	groups, err := api.GetGroups(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, group := range groups {
 | 
			
		||||
		fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/ims/ims.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/nlopes/slack/examples/ims/ims.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
 | 
			
		||||
	userID := "USER_ID"
 | 
			
		||||
 | 
			
		||||
	_, _, channelID, err := api.OpenIMChannel(userID)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.PostMessage(channelID, "Hello World!", slack.PostMessageParameters{})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/nlopes/slack/examples/messages/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/nlopes/slack/examples/messages/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	params := slack.PostMessageParameters{}
 | 
			
		||||
	attachment := slack.Attachment{
 | 
			
		||||
		Pretext: "some pretext",
 | 
			
		||||
		Text:    "some text",
 | 
			
		||||
		// Uncomment the following part to send a field too
 | 
			
		||||
		/*
 | 
			
		||||
			Fields: []slack.AttachmentField{
 | 
			
		||||
				slack.AttachmentField{
 | 
			
		||||
					Title: "a",
 | 
			
		||||
					Value: "no",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
	params.Attachments = []slack.Attachment{attachment}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage("CHANNEL_ID", "Some text", params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/nlopes/slack/examples/pins/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/nlopes/slack/examples/pins/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   WARNING: This example is destructive in the sense that it create a channel called testpinning
 | 
			
		||||
*/
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		postAsUserName  string
 | 
			
		||||
		postAsUserID    string
 | 
			
		||||
		postToChannelID string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Find the user to post as.
 | 
			
		||||
	authTest, err := api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting channels: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channelName := "testpinning"
 | 
			
		||||
 | 
			
		||||
	// Post as the authenticated user.
 | 
			
		||||
	postAsUserName = authTest.User
 | 
			
		||||
	postAsUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Create a temporary channel
 | 
			
		||||
	channel, err := api.CreateChannel(channelName)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// If the channel exists, that means we just need to unarchive it
 | 
			
		||||
		if err.Error() == "name_taken" {
 | 
			
		||||
			err = nil
 | 
			
		||||
			channels, err := api.GetChannels(false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("Could not retrieve channels")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			for _, archivedChannel := range channels {
 | 
			
		||||
				if archivedChannel.Name == channelName {
 | 
			
		||||
					if archivedChannel.IsArchived {
 | 
			
		||||
						err = api.UnarchiveChannel(archivedChannel.ID)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							fmt.Printf("Could not unarchive %s: %s\n", archivedChannel.ID, err)
 | 
			
		||||
							return
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					channel = &archivedChannel
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error setting test channel for pinning: %s\n", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	postToChannelID = channel.ID
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Posting as %s (%s) in channel %s\n", postAsUserName, postAsUserID, postToChannelID)
 | 
			
		||||
 | 
			
		||||
	// Post a message.
 | 
			
		||||
	postParams := slack.PostMessageParameters{}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage(postToChannelID, "Is this any good?", postParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error posting message: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Grab a reference to the message.
 | 
			
		||||
	msgRef := slack.NewRefToMessage(channelID, timestamp)
 | 
			
		||||
 | 
			
		||||
	// Add message pin to channel
 | 
			
		||||
	if err := api.AddPin(channelID, msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding pin: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all of the users pins.
 | 
			
		||||
	listPins, _, err := api.ListPins(channelID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error listing pins: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("All pins by %s...\n", authTest.User)
 | 
			
		||||
	for _, item := range listPins {
 | 
			
		||||
		fmt.Printf(" > Item type: %s\n", item.Type)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the pin.
 | 
			
		||||
	err = api.RemovePin(channelID, msgRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error remove pin: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = api.ArchiveChannel(channelID); err != nil {
 | 
			
		||||
		fmt.Printf("Error archiving channel: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								vendor/github.com/nlopes/slack/examples/reactions/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/nlopes/slack/examples/reactions/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		postAsUserName  string
 | 
			
		||||
		postAsUserID    string
 | 
			
		||||
		postToUserName  string
 | 
			
		||||
		postToUserID    string
 | 
			
		||||
		postToChannelID string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Find the user to post as.
 | 
			
		||||
	authTest, err := api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting channels: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Post as the authenticated user.
 | 
			
		||||
	postAsUserName = authTest.User
 | 
			
		||||
	postAsUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Posting to DM with self causes a conversation with slackbot.
 | 
			
		||||
	postToUserName = authTest.User
 | 
			
		||||
	postToUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Find the channel.
 | 
			
		||||
	_, _, chanID, err := api.OpenIMChannel(postToUserID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error opening IM: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	postToChannelID = chanID
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Posting as %s (%s) in DM with %s (%s), channel %s\n", postAsUserName, postAsUserID, postToUserName, postToUserID, postToChannelID)
 | 
			
		||||
 | 
			
		||||
	// Post a message.
 | 
			
		||||
	postParams := slack.PostMessageParameters{}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage(postToChannelID, "Is this any good?", postParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error posting message: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Grab a reference to the message.
 | 
			
		||||
	msgRef := slack.NewRefToMessage(channelID, timestamp)
 | 
			
		||||
 | 
			
		||||
	// React with :+1:
 | 
			
		||||
	if err := api.AddReaction("+1", msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// React with :-1:
 | 
			
		||||
	if err := api.AddReaction("cry", msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all reactions on the message.
 | 
			
		||||
	msgReactions, err := api.GetReactions(msgRef, slack.NewGetReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("%d reactions to message...\n", len(msgReactions))
 | 
			
		||||
	for _, r := range msgReactions {
 | 
			
		||||
		fmt.Printf("  %d users say %s\n", r.Count, r.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all of the users reactions.
 | 
			
		||||
	listReactions, _, err := api.ListReactions(slack.NewListReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error listing reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("All reactions by %s...\n", authTest.User)
 | 
			
		||||
	for _, item := range listReactions {
 | 
			
		||||
		fmt.Printf("%d on a %s...\n", len(item.Reactions), item.Type)
 | 
			
		||||
		for _, r := range item.Reactions {
 | 
			
		||||
			fmt.Printf("  %s (along with %d others)\n", r.Name, r.Count-1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the :cry: reaction.
 | 
			
		||||
	err = api.RemoveReaction("cry", msgRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error remove reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all reactions on the message.
 | 
			
		||||
	msgReactions, err = api.GetReactions(msgRef, slack.NewGetReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("%d reactions to message after removing cry...\n", len(msgReactions))
 | 
			
		||||
	for _, r := range msgReactions {
 | 
			
		||||
		fmt.Printf("  %d users say %s\n", r.Count, r.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/nlopes/slack/examples/stars/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/nlopes/slack/examples/stars/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all stars for the usr.
 | 
			
		||||
	params := slack.NewStarsParameters()
 | 
			
		||||
	starredItems, _, err := api.GetStarred(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting stars: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, s := range starredItems {
 | 
			
		||||
		var desc string
 | 
			
		||||
		switch s.Type {
 | 
			
		||||
		case slack.TYPE_MESSAGE:
 | 
			
		||||
			desc = s.Message.Text
 | 
			
		||||
		case slack.TYPE_FILE:
 | 
			
		||||
			desc = s.File.Name
 | 
			
		||||
		case slack.TYPE_FILE_COMMENT:
 | 
			
		||||
			desc = s.File.Name + " - " + s.Comment.Comment
 | 
			
		||||
		case slack.TYPE_CHANNEL, slack.TYPE_IM, slack.TYPE_GROUP:
 | 
			
		||||
			desc = s.Channel
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("Starred %s: %s\n", s.Type, desc)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/nlopes/slack/examples/team/team.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/nlopes/slack/examples/team/team.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	//Example for single user
 | 
			
		||||
	billingActive, err := api.GetBillableInfo("U023BECGF")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("ID: U023BECGF, BillingActive: %v\n\n\n", billingActive["U023BECGF"])
 | 
			
		||||
 | 
			
		||||
	//Example for team
 | 
			
		||||
	billingActiveForTeam, _ := api.GetBillableInfoForTeam()
 | 
			
		||||
	for id, value := range billingActiveForTeam {
 | 
			
		||||
		fmt.Printf("ID: %v, BillingActive: %v\n", id, value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/nlopes/slack/examples/users/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/nlopes/slack/examples/users/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	user, err := api.GetUserInfo("U023BECGF")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR TOKEN HERE")
 | 
			
		||||
	logger := log.New(os.Stdout, "slack-bot: ", log.Lshortfile|log.LstdFlags)
 | 
			
		||||
	slack.SetLogger(logger)
 | 
			
		||||
	api.SetDebug(true)
 | 
			
		||||
 | 
			
		||||
	rtm := api.NewRTM()
 | 
			
		||||
	go rtm.ManageConnection()
 | 
			
		||||
 | 
			
		||||
	for msg := range rtm.IncomingEvents {
 | 
			
		||||
		fmt.Print("Event Received: ")
 | 
			
		||||
		switch ev := msg.Data.(type) {
 | 
			
		||||
		case *slack.HelloEvent:
 | 
			
		||||
			// Ignore hello
 | 
			
		||||
 | 
			
		||||
		case *slack.ConnectedEvent:
 | 
			
		||||
			fmt.Println("Infos:", ev.Info)
 | 
			
		||||
			fmt.Println("Connection counter:", ev.ConnectionCount)
 | 
			
		||||
			// Replace C2147483705 with your Channel ID
 | 
			
		||||
			rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", "C2147483705"))
 | 
			
		||||
 | 
			
		||||
		case *slack.MessageEvent:
 | 
			
		||||
			fmt.Printf("Message: %v\n", ev)
 | 
			
		||||
 | 
			
		||||
		case *slack.PresenceChangeEvent:
 | 
			
		||||
			fmt.Printf("Presence Change: %v\n", ev)
 | 
			
		||||
 | 
			
		||||
		case *slack.LatencyReport:
 | 
			
		||||
			fmt.Printf("Current latency: %v\n", ev.Value)
 | 
			
		||||
 | 
			
		||||
		case *slack.RTMError:
 | 
			
		||||
			fmt.Printf("Error: %s\n", ev.Error())
 | 
			
		||||
 | 
			
		||||
		case *slack.InvalidAuthEvent:
 | 
			
		||||
			fmt.Printf("Invalid credentials")
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
 | 
			
		||||
			// Ignore other events..
 | 
			
		||||
			// fmt.Printf("Unexpected: %v\n", msg.Data)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -267,10 +267,7 @@ func (api *Client) DeleteFileContext(ctx context.Context, fileID string) error {
 | 
			
		||||
		"file":  {fileID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := fileRequest(ctx, "files.delete", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -208,10 +208,7 @@ func (api *Client) LeaveGroupContext(ctx context.Context, group string) error {
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest(ctx, "groups.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromGroup kicks a user from a group
 | 
			
		||||
@@ -227,10 +224,7 @@ func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user str
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest(ctx, "groups.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGroups retrieves all groups
 | 
			
		||||
@@ -289,10 +283,7 @@ func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest(ctx, "groups.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenGroup opens a private group
 | 
			
		||||
							
								
								
									
										0
									
								
								vendor/github.com/matterbridge/slack/im.go → vendor/github.com/nlopes/slack/im.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								vendor/github.com/matterbridge/slack/im.go → vendor/github.com/nlopes/slack/im.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,6 +3,7 @@ package slack
 | 
			
		||||
// OutgoingMessage is used for the realtime API, and seems incomplete.
 | 
			
		||||
type OutgoingMessage struct {
 | 
			
		||||
	ID              int    `json:"id"`
 | 
			
		||||
	// channel ID
 | 
			
		||||
	Channel         string `json:"channel,omitempty"`
 | 
			
		||||
	Text            string `json:"text,omitempty"`
 | 
			
		||||
	Type            string `json:"type,omitempty"`
 | 
			
		||||
@@ -121,12 +122,12 @@ type Pong struct {
 | 
			
		||||
// NewOutgoingMessage prepares an OutgoingMessage that the user can
 | 
			
		||||
// use to send a message. Use this function to properly set the
 | 
			
		||||
// messageID.
 | 
			
		||||
func (rtm *RTM) NewOutgoingMessage(text string, channel string) *OutgoingMessage {
 | 
			
		||||
func (rtm *RTM) NewOutgoingMessage(text string, channelID string) *OutgoingMessage {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	return &OutgoingMessage{
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		Type:    "message",
 | 
			
		||||
		Channel: channel,
 | 
			
		||||
		Channel: channelID,
 | 
			
		||||
		Text:    text,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -134,11 +135,11 @@ func (rtm *RTM) NewOutgoingMessage(text string, channel string) *OutgoingMessage
 | 
			
		||||
// NewTypingMessage prepares an OutgoingMessage that the user can
 | 
			
		||||
// use to send as a typing indicator. Use this function to properly set the
 | 
			
		||||
// messageID.
 | 
			
		||||
func (rtm *RTM) NewTypingMessage(channel string) *OutgoingMessage {
 | 
			
		||||
func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	return &OutgoingMessage{
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		Type:    "typing",
 | 
			
		||||
		Channel: channel,
 | 
			
		||||
		Channel: channelID,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								vendor/github.com/matterbridge/slack/misc.go → vendor/github.com/nlopes/slack/misc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/matterbridge/slack/misc.go → vendor/github.com/nlopes/slack/misc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,7 @@ import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -42,6 +43,14 @@ func (s WebError) Error() string {
 | 
			
		||||
	return string(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RateLimitedError struct {
 | 
			
		||||
	RetryAfter time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *RateLimitedError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Slack rate limit exceeded, retry after %s", e.RetryAfter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) {
 | 
			
		||||
	body := &bytes.Buffer{}
 | 
			
		||||
	wr := multipart.NewWriter(body)
 | 
			
		||||
@@ -79,12 +88,7 @@ func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error
 | 
			
		||||
		logger.Printf("parseResponseBody: %s\n", string(response))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(response, &intf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return json.Unmarshal(response, &intf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
 | 
			
		||||
@@ -112,8 +116,16 @@ func postWithMultipartResponse(ctx context.Context, path, name, fieldname string
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode == http.StatusTooManyRequests {
 | 
			
		||||
		retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return &RateLimitedError{time.Duration(retry) * time.Second}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		logResponse(resp, debug)
 | 
			
		||||
		return fmt.Errorf("Slack server error: %s.", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
@@ -136,8 +148,16 @@ func postForm(ctx context.Context, endpoint string, values url.Values, intf inte
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode == http.StatusTooManyRequests {
 | 
			
		||||
		retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return &RateLimitedError{time.Duration(retry) * time.Second}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
	if resp.StatusCode != http.StatusOK {
 | 
			
		||||
		logResponse(resp, debug)
 | 
			
		||||
		return fmt.Errorf("Slack server error: %s.", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/matterbridge/slack/rtm.go → vendor/github.com/nlopes/slack/rtm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/matterbridge/slack/rtm.go → vendor/github.com/nlopes/slack/rtm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -27,17 +27,8 @@ func (api *Client) StartRTMContext(ctx context.Context) (info *Info, websocketUR
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, "", response.Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// websocket.Dial does not accept url without the port (yet)
 | 
			
		||||
	// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
 | 
			
		||||
	// but slack returns the address with no port, so we have to fix it
 | 
			
		||||
	api.Debugln("Using URL:", response.Info.URL)
 | 
			
		||||
	websocketURL, err = websocketizeURLPort(response.Info.URL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("parsing response URL: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &response.Info, websocketURL, nil
 | 
			
		||||
	return &response.Info, response.Info.URL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectRTM calls the "rtm.connect" endpoint and returns the provided URL and the compact Info block.
 | 
			
		||||
@@ -59,17 +50,8 @@ func (api *Client) ConnectRTMContext(ctx context.Context) (info *Info, websocket
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, "", response.Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// websocket.Dial does not accept url without the port (yet)
 | 
			
		||||
	// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
 | 
			
		||||
	// but slack returns the address with no port, so we have to fix it
 | 
			
		||||
	api.Debugln("Using URL:", response.Info.URL)
 | 
			
		||||
	websocketURL, err = websocketizeURLPort(response.Info.URL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("parsing response URL: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &response.Info, websocketURL, nil
 | 
			
		||||
	return &response.Info, response.Info.URL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRTM returns a RTM, which provides a fully managed connection to
 | 
			
		||||
@@ -90,6 +72,7 @@ func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM {
 | 
			
		||||
		isConnected:      false,
 | 
			
		||||
		wasIntentional:   true,
 | 
			
		||||
		killChannel:      make(chan bool),
 | 
			
		||||
		disconnected:     make(chan struct{}),
 | 
			
		||||
		forcePing:        make(chan bool),
 | 
			
		||||
		rawEvents:        make(chan json.RawMessage),
 | 
			
		||||
		idGen:            NewSafeID(1),
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/matterbridge/slack/slack.go → vendor/github.com/nlopes/slack/slack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/matterbridge/slack/slack.go → vendor/github.com/nlopes/slack/slack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,12 +3,13 @@ package slack
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var logger *log.Logger // A logger that can be set by consumers
 | 
			
		||||
var logger stdLogger // A logger that can be set by consumers
 | 
			
		||||
/*
 | 
			
		||||
  Added as a var so that we can change this for testing purposes
 | 
			
		||||
*/
 | 
			
		||||
@@ -41,12 +42,31 @@ type Client struct {
 | 
			
		||||
	debug bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stdLogger is a logger interface compatible with both stdlib and some
 | 
			
		||||
// 3rd party loggers such as logrus.
 | 
			
		||||
type stdLogger interface {
 | 
			
		||||
	Print(...interface{})
 | 
			
		||||
	Printf(string, ...interface{})
 | 
			
		||||
	Println(...interface{})
 | 
			
		||||
 | 
			
		||||
	Fatal(...interface{})
 | 
			
		||||
	Fatalf(string, ...interface{})
 | 
			
		||||
	Fatalln(...interface{})
 | 
			
		||||
 | 
			
		||||
	Panic(...interface{})
 | 
			
		||||
	Panicf(string, ...interface{})
 | 
			
		||||
	Panicln(...interface{})
 | 
			
		||||
 | 
			
		||||
	Output(int, string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLogger let's library users supply a logger, so that api debugging
 | 
			
		||||
// can be logged along with the application's debugging info.
 | 
			
		||||
func SetLogger(l *log.Logger) {
 | 
			
		||||
func SetLogger(l stdLogger) {
 | 
			
		||||
	logger = l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates new Client.
 | 
			
		||||
func New(token string) *Client {
 | 
			
		||||
	s := &Client{}
 | 
			
		||||
	s.config.token = token
 | 
			
		||||
@@ -83,12 +103,12 @@ func (api *Client) SetDebug(debug bool) {
 | 
			
		||||
 | 
			
		||||
func (api *Client) Debugf(format string, v ...interface{}) {
 | 
			
		||||
	if api.debug {
 | 
			
		||||
		logger.Printf(format, v...)
 | 
			
		||||
		logger.Output(2, fmt.Sprintf(format, v...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) Debugln(v ...interface{}) {
 | 
			
		||||
	if api.debug {
 | 
			
		||||
		logger.Println(v...)
 | 
			
		||||
		logger.Output(2, fmt.Sprintln(v...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -200,10 +200,7 @@ func (api *Client) SetUserAsActiveContext(ctx context.Context) error {
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := userRequest(ctx, "users.setActive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPresence changes the currently authenticated user presence
 | 
			
		||||
@@ -247,8 +244,8 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityRes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPhoto changes the currently authenticated user's profile image
 | 
			
		||||
func (api *Client) SetUserPhoto(ctx context.Context, image string, params UserSetPhotoParams) error {
 | 
			
		||||
	return api.SetUserPhoto(context.Background(), image, params)
 | 
			
		||||
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
 | 
			
		||||
	return api.SetUserPhotoContext(context.Background(), image, params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
 | 
			
		||||
@@ -27,6 +27,7 @@ type RTM struct {
 | 
			
		||||
	IncomingEvents   chan RTMEvent
 | 
			
		||||
	outgoingMessages chan OutgoingMessage
 | 
			
		||||
	killChannel      chan bool
 | 
			
		||||
	disconnected     chan struct{} // disconnected is closed when Disconnect is invoked, regardless of connection state. Allows for ManagedConnection to not leak.
 | 
			
		||||
	forcePing        chan bool
 | 
			
		||||
	rawEvents        chan json.RawMessage
 | 
			
		||||
	wasIntentional   bool
 | 
			
		||||
@@ -59,9 +60,14 @@ type RTMOptions struct {
 | 
			
		||||
 | 
			
		||||
// Disconnect and wait, blocking until a successful disconnection.
 | 
			
		||||
func (rtm *RTM) Disconnect() error {
 | 
			
		||||
	// this channel is always closed on disconnect. lets the ManagedConnection() function
 | 
			
		||||
	// properly clean up.
 | 
			
		||||
	close(rtm.disconnected)
 | 
			
		||||
 | 
			
		||||
	if !rtm.isConnected {
 | 
			
		||||
		return errors.New("Invalid call to Disconnect - Slack API is already disconnected")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtm.killChannel <- true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -99,6 +99,15 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
 | 
			
		||||
			Attempt:  boff.attempts,
 | 
			
		||||
			ErrorObj: err,
 | 
			
		||||
		}}
 | 
			
		||||
 | 
			
		||||
		// check if Disconnect() has been invoked.
 | 
			
		||||
		select {
 | 
			
		||||
		case _ = <-rtm.disconnected:
 | 
			
		||||
			rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{Intentional: true}}
 | 
			
		||||
			return nil, nil, fmt.Errorf("disconnect received while trying to connect")
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// get time we should wait before attempting to connect again
 | 
			
		||||
		dur := boff.Duration()
 | 
			
		||||
		rtm.Debugf("reconnection %d failed: %s", boff.attempts+1, err)
 | 
			
		||||
@@ -124,7 +133,8 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (*Info, *websocket.Conn, error
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, err := websocketProxyDial(url, "http://api.slack.com")
 | 
			
		||||
	// Only use HTTPS for connections to prevent MITM attacks on the connection.
 | 
			
		||||
	conn, err := websocketProxyDial(url, "https://api.slack.com")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -317,10 +327,13 @@ func (rtm *RTM) handleAck(event json.RawMessage) {
 | 
			
		||||
		rtm.Debugln(" -> Erroneous 'ack' event:", string(event))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ack.Ok {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"ack", ack}
 | 
			
		||||
	} else {
 | 
			
		||||
	} else if ack.RTMResponse.Error != nil {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{ack.Error}}
 | 
			
		||||
	} else {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{fmt.Errorf("ack decode failure")}}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -32,8 +32,7 @@ func websocketHTTPConnect(proxy, urlString string) (net.Conn, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cc := httputil.NewProxyClientConn(p, nil)
 | 
			
		||||
	cc.Do(&req)
 | 
			
		||||
	if err != nil && err != httputil.ErrPersistEOF {
 | 
			
		||||
	if _, err := cc.Do(&req); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							@@ -295,11 +295,11 @@
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/matterbridge/slack",
 | 
			
		||||
			"repository": "https://github.com/matterbridge/slack",
 | 
			
		||||
			"importpath": "github.com/matterbridge/gomatrix",
 | 
			
		||||
			"repository": "https://github.com/matterbridge/gomatrix",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "1c6e6305bf9c07fc603c9cf28f09ab0517a03120",
 | 
			
		||||
			"branch": "matterbridge",
 | 
			
		||||
			"revision": "78ac6a1a0f5fb9eb1684d85a3ed581a742fe4c79",
 | 
			
		||||
			"branch": "work",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@@ -441,6 +441,14 @@
 | 
			
		||||
			"path": "/i18n",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/nlopes/slack",
 | 
			
		||||
			"repository": "https://github.com/nlopes/slack",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "107290b5bbaf3e634833346bb4ff389b1c782bc7",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/paulrosania/go-charset",
 | 
			
		||||
			"repository": "https://github.com/paulrosania/go-charset",
 | 
			
		||||
@@ -712,7 +720,7 @@
 | 
			
		||||
			"importpath": "golang.org/x/net/websocket",
 | 
			
		||||
			"repository": "https://go.googlesource.com/net",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "6c23252515492caf9b228a9d5cabcdbde29f7f82",
 | 
			
		||||
			"revision": "434ec0c7fe3742c984919a691b2018a6e9694425",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"path": "/websocket",
 | 
			
		||||
			"notests": true
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user