forked from lug/matterbridge
		
	Compare commits
	
		
			98 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 8eb5e3cbf8 | ||
|   | ddc2625934 | ||
|   | 7f7ca697a0 | ||
|   | 900375679b | ||
|   | 9440b9e313 | ||
|   | 393f9e998b | ||
|   | ba0bfe70a8 | ||
|   | 3c4a3e3f75 | ||
|   | 274fb09ed4 | ||
|   | d44598a900 | ||
|   | c9cfa59f54 | ||
|   | 7062234331 | ||
|   | 9754569525 | ||
|   | 52a071e34d | ||
|   | 2d8f749e36 | ||
|   | a18cb74f03 | ||
|   | 6c442e239d | ||
|   | eaf92fca4d | ||
|   | 06b7bad714 | ||
|   | 19eec2ed03 | ||
|   | d99c54343a | ||
|   | 308a110000 | ||
|   | 4f406b2ce6 | ||
|   | e564c555d7 | ||
|   | f7ec9af9e8 | ||
|   | 4d93a774ce | ||
|   | 2595dd30bf | ||
|   | 9190365289 | ||
|   | 57794b3b9f | ||
|   | 3c36f651be | ||
|   | 8e6ddadba2 | ||
|   | 8a87a71927 | ||
|   | 0047e6f523 | ||
|   | 7183095a28 | ||
|   | 13c90893c7 | ||
|   | 976fbcd07f | ||
|   | d97b077e85 | ||
|   | 8950575bfb | ||
|   | 11fc4c286f | ||
|   | 8d08e348a9 | ||
|   | a18807f19e | ||
|   | 29f658fd3c | ||
|   | a30bb8fed0 | ||
|   | 092ca1cd67 | ||
|   | 0df2539641 | ||
|   | 0f2d8a599c | ||
|   | 54b3143a1d | ||
|   | 148f7d2a91 | ||
|   | 1aa662f763 | ||
|   | 0b86b88de7 | ||
|   | 98033b1ba7 | ||
|   | 2b7eab629d | ||
|   | 0e4973e15c | ||
|   | af0acf0dae | ||
|   | 76e5fe5a87 | ||
|   | 802c80f40c | ||
|   | a51c5bd905 | ||
|   | 8c68556f52 | ||
|   | cca1ea2404 | ||
|   | 281016a501 | ||
|   | d4acdf2f89 | ||
|   | 0951e75c85 | ||
|   | 6b017b226a | ||
|   | 9e3bd7398c | ||
|   | 79f764c7a8 | ||
|   | b5dc4353fb | ||
|   | 2fbac73c29 | ||
|   | 6616d105d1 | ||
|   | 6b4b19194e | ||
|   | 9785edd263 | ||
|   | 2a0bc11b68 | ||
|   | dd0325a88d | ||
|   | 20783c0978 | ||
|   | 3f06a40bd5 | ||
|   | 68f43985ad | ||
|   | 915ca8f817 | ||
|   | a65a81610b | ||
|   | 8eb6ed5639 | ||
|   | 795a8705c3 | ||
|   | 3af0dc3b3a | ||
|   | 9cf9b958a3 | ||
|   | 3ac2ba8d5a | ||
|   | d893421c7b | ||
|   | 250b3bb579 | ||
|   | e9edbfc051 | ||
|   | e343db6f72 | ||
|   | 4d57d66f85 | ||
|   | 54ed6320c2 | ||
|   | 23083f3ae0 | ||
|   | 1985873494 | ||
|   | 8ae5917659 | ||
|   | c91bfd08d8 | ||
|   | 49110a5872 | ||
|   | c01c8edeb8 | ||
|   | ff8cf067b8 | ||
|   | 1420f68050 | ||
|   | c0be3e585a | ||
|   | 3049ef9151 | 
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/Bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/Bug_report.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| --- | ||||
| name: Bug report | ||||
| about: Create a report to help us improve. (Check the FAQ on the wiki first) | ||||
| labels: bug | ||||
|  | ||||
| --- | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/Feature_request.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/Feature_request.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| --- | ||||
| name: Feature request | ||||
| about: Suggest an idea for this project | ||||
| labels: enhancement | ||||
|  | ||||
| --- | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,7 @@ run: | ||||
|   # default value is empty list, but next dirs are always skipped independently | ||||
|   # from this option's value: | ||||
|   #   	vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ | ||||
|   skip-dirs: | ||||
|   skip-dirs: gateway/bridgemap$ | ||||
|  | ||||
|   # which files to skip: they will be analyzed, but issues from them | ||||
|   # won't be reported. Default value is empty list, but there is | ||||
| @@ -175,6 +175,7 @@ linters: | ||||
|     - maligned | ||||
|     - prealloc | ||||
|     - wsl | ||||
|     - gomnd | ||||
|  | ||||
|  | ||||
| # rules to deal with reported isues | ||||
|   | ||||
							
								
								
									
										12
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -20,22 +20,22 @@ jobs: | ||||
|   - stage: lint | ||||
|     # Run linting in one Go environment only. | ||||
|     script: ./ci/lint.sh | ||||
|     go: 1.13.x | ||||
|     go: 1.14.x | ||||
|     env: | ||||
|     - GO111MODULE=on | ||||
|     - GOLANGCI_VERSION="v1.21.0" | ||||
|     - GOLANGCI_VERSION="v1.23.7" | ||||
|   - stage: test | ||||
|     # Run tests in a combination of Go environments. | ||||
|     script: ./ci/test.sh | ||||
|     go: 1.12.x | ||||
|     go: 1.13.x | ||||
|     env: | ||||
|     - GO111MODULE=off | ||||
|     - GOFLAGS=-mod=vendor | ||||
|   - script: ./ci/test.sh | ||||
|     go: 1.12.x | ||||
|     go: 1.13.x | ||||
|     env: | ||||
|     - GO111MODULE=on | ||||
|   - script: ./ci/test.sh | ||||
|     go: 1.13.x | ||||
|     go: 1.14.x | ||||
|     env: | ||||
|     - GO111MODULE=on | ||||
|     - REPORT_COVERAGE=1 | ||||
|   | ||||
							
								
								
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,11 +1,16 @@ | ||||
| FROM alpine:edge | ||||
| ENTRYPOINT ["/bin/matterbridge"] | ||||
| FROM alpine:edge AS builder | ||||
|  | ||||
| COPY . /go/src/github.com/42wim/matterbridge | ||||
| RUN apk update && apk add go git gcc musl-dev ca-certificates mailcap \ | ||||
| RUN apk update && apk add go git gcc musl-dev \ | ||||
|         && 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 | ||||
|         && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge | ||||
|  | ||||
| FROM alpine:edge | ||||
| RUN apk --no-cache add ca-certificates mailcap | ||||
| COPY --from=builder /bin/matterbridge /bin/matterbridge | ||||
| RUN mkdir /etc/matterbridge | ||||
| RUN touch /etc/matterbridge/matterbridge.toml | ||||
| RUN ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml | ||||
| ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"] | ||||
|   | ||||
							
								
								
									
										79
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								README.md
									
									
									
									
									
								
							| @@ -9,19 +9,20 @@ Letting people be where they want to be.<br /> | ||||
|  | ||||
|    <sup> | ||||
|  | ||||
| [Discord][mb-discord] | | ||||
| [Gitter][mb-gitter] | | ||||
| [IRC][mb-irc] | | ||||
| [Discord][mb-discord] | | ||||
| [Keybase][mb-keybase] | | ||||
| [Matrix][mb-matrix] | | ||||
| [Slack][mb-slack] | | ||||
| [Mattermost][mb-mattermost] | | ||||
| [MSTeams][mb-msteams] | | ||||
| [Rocket.Chat][mb-rocketchat] | | ||||
| [XMPP][mb-xmpp] | | ||||
| [Slack][mb-slack] | | ||||
| [Telegram][mb-telegram] | | ||||
| [Twitch][mb-twitch] | | ||||
| [WhatsApp][mb-whatsapp] | | ||||
| [XMPP][mb-xmpp] | | ||||
| [Zulip][mb-zulip] | | ||||
| [Telegram][mb-telegram] | | ||||
| [Keybase][mb-keybase] | | ||||
| And more... | ||||
| </sup> | ||||
|  | ||||
| @@ -86,28 +87,29 @@ And more... | ||||
|  | ||||
| ### Natively supported | ||||
|  | ||||
| - [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x | ||||
| - [IRC](http://www.mirc.com/servers.html) | ||||
| - [XMPP](https://xmpp.org) | ||||
| - [Gitter](https://gitter.im) | ||||
| - [Slack](https://slack.com) | ||||
| - [Discord](https://discordapp.com) | ||||
| - [Telegram](https://telegram.org) | ||||
| - [Rocket.chat](https://rocket.chat) | ||||
| - [Matrix](https://matrix.org) | ||||
| - [Steam](https://store.steampowered.com/) | ||||
| - [Twitch](https://twitch.tv) | ||||
| - [Ssh-chat](https://github.com/shazow/ssh-chat) | ||||
| - [WhatsApp](https://www.whatsapp.com/) | ||||
| - [Zulip](https://zulipchat.com) | ||||
| - [Gitter](https://gitter.im) | ||||
| - [IRC](http://www.mirc.com/servers.html) | ||||
| - [Keybase](https://keybase.io) | ||||
| - [Matrix](https://matrix.org) | ||||
| - [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x | ||||
| - [Microsoft Teams](https://teams.microsoft.com) | ||||
| - [Rocket.chat](https://rocket.chat) | ||||
| - [Slack](https://slack.com) | ||||
| - [Ssh-chat](https://github.com/shazow/ssh-chat) | ||||
| - [Steam](https://store.steampowered.com/) | ||||
| - [Telegram](https://telegram.org) | ||||
| - [Twitch](https://twitch.tv) | ||||
| - [WhatsApp](https://www.whatsapp.com/) | ||||
| - [XMPP](https://xmpp.org) | ||||
| - [Zulip](https://zulipchat.com) | ||||
|  | ||||
| ### 3rd party via matterbridge api | ||||
|  | ||||
| - [Discourse](https://github.com/DeclanHoare/matterbabble) | ||||
| - [Facebook messenger](https://github.com/VictorNine/fbridge) | ||||
| - [Minecraft](https://github.com/elytra/MatterLink) | ||||
| - [Reddit](https://github.com/bonehurtingjuice/mattereddit) | ||||
| - [Facebook messenger](https://github.com/VictorNine/fbridge) | ||||
| - [Discourse](https://github.com/DeclanHoare/matterbabble) | ||||
| - [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430) | ||||
|  | ||||
| ### API | ||||
| @@ -128,17 +130,18 @@ Used by the projects below. Feel free to make a PR to add your project to this l | ||||
|  | ||||
| Questions or want to test on your favorite platform? Join below: | ||||
|  | ||||
| - [Discord][mb-discord] | ||||
| - [Gitter][mb-gitter] | ||||
| - [IRC][mb-irc] | ||||
| - [Discord][mb-discord] | ||||
| - [Keybase][mb-keybase] | ||||
| - [Matrix][mb-matrix] | ||||
| - [Slack][mb-slack] | ||||
| - [Mattermost][mb-mattermost] | ||||
| - [Rocket.Chat][mb-rocketchat] | ||||
| - [XMPP][mb-xmpp] (matterbridge@conference.jabber.de) | ||||
| - [Twitch][mb-twitch] | ||||
| - [Zulip][mb-zulip] | ||||
| - [Slack][mb-slack] | ||||
| - [Telegram][mb-telegram] | ||||
| - [Twitch][mb-twitch] | ||||
| - [XMPP][mb-xmpp] (matterbridge@conference.jabber.de) | ||||
| - [Zulip][mb-zulip] | ||||
|  | ||||
| ## Screenshots | ||||
|  | ||||
| @@ -148,7 +151,7 @@ See https://github.com/42wim/matterbridge/wiki | ||||
|  | ||||
| ### Binaries | ||||
|  | ||||
| - Latest stable release [v1.16.5](https://github.com/42wim/matterbridge/releases/latest) | ||||
| - Latest stable release [v1.17.5](https://github.com/42wim/matterbridge/releases/latest) | ||||
| - Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/) | ||||
|  | ||||
| To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. | ||||
| @@ -321,30 +324,32 @@ Matterbridge wouldn't exist without these libraries: | ||||
| - gops - https://github.com/google/gops | ||||
| - gozulipbot - https://github.com/ifo/gozulipbot | ||||
| - irc - https://github.com/lrstanley/girc | ||||
| - mattermost - https://github.com/mattermost/mattermost-server | ||||
| - keybase - https://github.com/keybase/go-keybase-chat-bot | ||||
| - matrix - https://github.com/matrix-org/gomatrix | ||||
| - sshchat - https://github.com/shazow/ssh-chat | ||||
| - mattermost - https://github.com/mattermost/mattermost-server | ||||
| - msgraph.go - https://github.com/yaegashi/msgraph.go | ||||
| - slack - https://github.com/nlopes/slack | ||||
| - sshchat - https://github.com/shazow/ssh-chat | ||||
| - steam - https://github.com/Philipp15b/go-steam | ||||
| - telegram - https://github.com/go-telegram-bot-api/telegram-bot-api | ||||
| - xmpp - https://github.com/mattn/go-xmpp | ||||
| - whatsapp - https://github.com/Rhymen/go-whatsapp/ | ||||
| - zulip - https://github.com/ifo/gozulipbot | ||||
| - tengo - https://github.com/d5/tengo | ||||
| - keybase - https://github.com/keybase/go-keybase-chat-bot | ||||
| - whatsapp - https://github.com/Rhymen/go-whatsapp/ | ||||
| - xmpp - https://github.com/mattn/go-xmpp | ||||
| - zulip - https://github.com/ifo/gozulipbot | ||||
|  | ||||
| <!-- Links --> | ||||
|  | ||||
| [mb-discord]: https://discord.gg/AkKPtrQ | ||||
| [mb-gitter]: https://gitter.im/42wim/matterbridge | ||||
| [mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat | ||||
| [mb-discord]: https://discord.gg/AkKPtrQ | ||||
| [mb-keybase]: https://keybase.io/team/matterbridge | ||||
| [mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org | ||||
| [mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA | ||||
| [mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e | ||||
| [mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7 | ||||
| [mb-rocketchat]: https://open.rocket.chat/channel/matterbridge | ||||
| [mb-xmpp]: https://inverse.chat/ | ||||
| [mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA | ||||
| [mb-telegram]: https://t.me/Matterbridge | ||||
| [mb-twitch]: https://www.twitch.tv/matterbridge | ||||
| [mb-whatsapp]: https://www.whatsapp.com/ | ||||
| [mb-keybase]: https://keybase.io | ||||
| [mb-xmpp]: https://inverse.chat/ | ||||
| [mb-zulip]: https://matterbridge.zulipchat.com/register/ | ||||
| [mb-telegram]: https://t.me/Matterbridge | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"log" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| @@ -74,6 +75,7 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map | ||||
| 	for ID, channel := range channels { | ||||
| 		if !exists[ID] { | ||||
| 			b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID) | ||||
| 			time.Sleep(time.Duration(b.GetInt("JoinDelay")) * time.Millisecond) | ||||
| 			err := b.JoinChannel(channel) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package config | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| @@ -76,17 +77,20 @@ type Protocol struct { | ||||
| 	BindAddress            string // mattermost, slack // DEPRECATED | ||||
| 	Buffer                 int    // api | ||||
| 	Charset                string // irc | ||||
| 	ClientID               string // msteams | ||||
| 	ColorNicks             bool   // only irc for now | ||||
| 	Debug                  bool   // general | ||||
| 	DebugLevel             int    // only for irc now | ||||
| 	DisableWebPagePreview  bool   // telegram | ||||
| 	EditSuffix             string // mattermost, slack, discord, telegram, gitter | ||||
| 	EditDisable            bool   // mattermost, slack, discord, telegram, gitter | ||||
| 	HTMLDisable            bool   // matrix | ||||
| 	IconURL                string // mattermost, slack | ||||
| 	IgnoreFailureOnStart   bool   // general | ||||
| 	IgnoreNicks            string // all protocols | ||||
| 	IgnoreMessages         string // all protocols | ||||
| 	Jid                    string // xmpp | ||||
| 	JoinDelay              string // all protocols | ||||
| 	Label                  string // all protocols | ||||
| 	Login                  string // mattermost, matrix | ||||
| 	MediaDownloadBlackList []string | ||||
| @@ -124,6 +128,7 @@ type Protocol struct { | ||||
| 	RemoteNickFormat       string     // all protocols | ||||
| 	RunCommands            []string   // IRC | ||||
| 	Server                 string     // IRC,mattermost,XMPP,discord | ||||
| 	SessionFile            string     // msteams,whatsapp | ||||
| 	ShowJoinPart           bool       // all protocols | ||||
| 	ShowTopicChange        bool       // slack | ||||
| 	ShowUserTyping         bool       // slack | ||||
| @@ -131,13 +136,17 @@ type Protocol struct { | ||||
| 	SkipTLSVerify          bool       // IRC, mattermost | ||||
| 	SkipVersionCheck       bool       // mattermost | ||||
| 	StripNick              bool       // all protocols | ||||
| 	StripMarkdown          bool       // irc | ||||
| 	SyncTopic              bool       // slack | ||||
| 	TengoModifyMessage     string     // general | ||||
| 	Team                   string     // mattermost, keybase | ||||
| 	TeamID                 string     // msteams | ||||
| 	TenantID               string     // msteams | ||||
| 	Token                  string     // gitter, slack, discord, api | ||||
| 	Topic                  string     // zulip | ||||
| 	URL                    string     // mattermost, slack // DEPRECATED | ||||
| 	UseAPI                 bool       // mattermost, slack | ||||
| 	UseLocalAvatar         []string   // discord | ||||
| 	UseSASL                bool       // IRC | ||||
| 	UseTLS                 bool       // IRC | ||||
| 	UseDiscriminator       bool       // discord | ||||
| @@ -235,7 +244,8 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config { | ||||
| 		logger.Fatalf("Failed to read configuration file: %#v", err) | ||||
| 	} | ||||
|  | ||||
| 	mycfg := newConfigFromString(logger, input) | ||||
| 	cfgtype := detectConfigType(cfgfile) | ||||
| 	mycfg := newConfigFromString(logger, input, cfgtype) | ||||
| 	if mycfg.cv.General.MediaDownloadSize == 0 { | ||||
| 		mycfg.cv.General.MediaDownloadSize = 1000000 | ||||
| 	} | ||||
| @@ -246,14 +256,26 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config { | ||||
| 	return mycfg | ||||
| } | ||||
|  | ||||
| // detectConfigType detects JSON and YAML formats, defaults to TOML. | ||||
| func detectConfigType(cfgfile string) string { | ||||
| 	fileExt := filepath.Ext(cfgfile) | ||||
| 	switch fileExt { | ||||
| 	case ".json": | ||||
| 		return "json" | ||||
| 	case ".yaml", ".yml": | ||||
| 		return "yaml" | ||||
| 	} | ||||
| 	return "toml" | ||||
| } | ||||
|  | ||||
| // NewConfigFromString instantiates a new configuration based on the specified string. | ||||
| func NewConfigFromString(rootLogger *logrus.Logger, input []byte) Config { | ||||
| 	logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"}) | ||||
| 	return newConfigFromString(logger, input) | ||||
| 	return newConfigFromString(logger, input, "toml") | ||||
| } | ||||
|  | ||||
| func newConfigFromString(logger *logrus.Entry, input []byte) *config { | ||||
| 	viper.SetConfigType("toml") | ||||
| func newConfigFromString(logger *logrus.Entry, input []byte, cfgtype string) *config { | ||||
| 	viper.SetConfigType(cfgtype) | ||||
| 	viper.SetEnvPrefix("matterbridge") | ||||
| 	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) | ||||
| 	viper.AutomaticEnv() | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/bwmarrin/discordgo" | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| ) | ||||
|  | ||||
| const MessageLength = 1950 | ||||
| @@ -21,6 +21,7 @@ type Bdiscord struct { | ||||
| 	c *discordgo.Session | ||||
|  | ||||
| 	nick            string | ||||
| 	userID          string | ||||
| 	guildID         string | ||||
| 	webhookID       string | ||||
| 	webhookToken    string | ||||
| @@ -33,6 +34,8 @@ type Bdiscord struct { | ||||
| 	membersMutex  sync.RWMutex | ||||
| 	userMemberMap map[string]*discordgo.Member | ||||
| 	nickMemberMap map[string]*discordgo.Member | ||||
| 	webhookCache  map[string]string | ||||
| 	webhookMutex  sync.RWMutex | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| @@ -40,6 +43,7 @@ func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	b.userMemberMap = make(map[string]*discordgo.Member) | ||||
| 	b.nickMemberMap = make(map[string]*discordgo.Member) | ||||
| 	b.channelInfoMap = make(map[string]*config.ChannelInfo) | ||||
| 	b.webhookCache = make(map[string]string) | ||||
| 	if b.GetString("WebhookURL") != "" { | ||||
| 		b.Log.Debug("Configuring Discord Incoming Webhook") | ||||
| 		b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL")) | ||||
| @@ -92,6 +96,7 @@ func (b *Bdiscord) Connect() error { | ||||
| 	} | ||||
| 	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1) | ||||
| 	b.nick = userinfo.Username | ||||
| 	b.userID = userinfo.ID | ||||
| 	b.channelsMutex.Lock() | ||||
| 	for _, guild := range guilds { | ||||
| 		if guild.Name == serverName || guild.ID == serverName { | ||||
| @@ -114,30 +119,37 @@ func (b *Bdiscord) Connect() error { | ||||
| 			b.Log.Infof("Server=\"%s\" # Server ID", guild.ID) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	if b.GetString("WebhookURL") == "" { | ||||
| 		for _, channel := range b.channels { | ||||
| 			b.Log.Debugf("found channel %#v", channel) | ||||
| 		} | ||||
| 	} else { | ||||
| 		b.canEditWebhooks = true | ||||
| 		for _, channel := range b.channels { | ||||
| 			b.Log.Debugf("found channel %#v; verifying PermissionManageWebhooks", channel) | ||||
| 			perms, permsErr := b.c.State.UserChannelPermissions(userinfo.ID, channel.ID) | ||||
| 			manageWebhooks := discordgo.PermissionManageWebhooks | ||||
| 			if permsErr != nil || perms&manageWebhooks != manageWebhooks { | ||||
| 				b.Log.Warnf("Can't manage webhooks in channel \"%s\"", channel.Name) | ||||
| 				b.canEditWebhooks = false | ||||
| 		manageWebhooks := discordgo.PermissionManageWebhooks | ||||
| 		var channelsDenied []string | ||||
| 		for _, info := range b.Channels { | ||||
| 			id := b.getChannelID(info.Name) // note(qaisjp): this readlocks channelsMutex | ||||
| 			b.Log.Debugf("Verifying PermissionManageWebhooks for %s with ID %s", info.ID, id) | ||||
|  | ||||
| 			perms, permsErr := b.c.UserChannelPermissions(userinfo.ID, id) | ||||
| 			if permsErr != nil { | ||||
| 				b.Log.Warnf("Failed to check PermissionManageWebhooks in channel \"%s\": %s", info.Name, permsErr.Error()) | ||||
| 			} else if perms&manageWebhooks == manageWebhooks { | ||||
| 				continue | ||||
| 			} | ||||
| 			channelsDenied = append(channelsDenied, fmt.Sprintf("%#v", info.Name)) | ||||
| 		} | ||||
|  | ||||
| 		b.canEditWebhooks = len(channelsDenied) == 0 | ||||
| 		if b.canEditWebhooks { | ||||
| 			b.Log.Info("Can manage webhooks; will edit channel for global webhook on send") | ||||
| 		} else { | ||||
| 			b.Log.Warn("Can't manage webhooks; won't edit channel for global webhook on send") | ||||
| 			b.Log.Warn("Can't manage webhooks in channels: ", strings.Join(channelsDenied, ", ")) | ||||
| 		} | ||||
| 	} | ||||
| 	b.channelsMutex.RUnlock() | ||||
| @@ -179,6 +191,8 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error { | ||||
| func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | ||||
|  | ||||
| 	origMsgID := msg.ID | ||||
|  | ||||
| 	channelID := b.getChannelID(msg.Channel) | ||||
| 	if channelID == "" { | ||||
| 		return "", fmt.Errorf("Could not find channelID for %v", msg.Channel) | ||||
| @@ -215,12 +229,13 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 	// Use webhook to send the message | ||||
| 	if wID != "" && msg.Event != config.EventMsgDelete { | ||||
| 		// skip events | ||||
| 		if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { | ||||
| 		if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		// If we are editing a message, delete the old message | ||||
| 		if msg.ID != "" { | ||||
| 			msg.ID = b.getCacheID(msg.ID) | ||||
| 			b.Log.Debugf("Deleting edited webhook message") | ||||
| 			err := b.c.ChannelMessageDelete(channelID, msg.ID) | ||||
| 			if err != nil { | ||||
| @@ -264,6 +279,8 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 		if msg == nil { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		b.updateCacheID(origMsgID, msg.ID) | ||||
| 		return msg.ID, nil | ||||
| 	} | ||||
|  | ||||
| @@ -274,6 +291,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 		if msg.ID == "" { | ||||
| 			return "", nil | ||||
| 		} | ||||
| 		msg.ID = b.getCacheID(msg.ID) | ||||
| 		err := b.c.ChannelMessageDelete(channelID, msg.ID) | ||||
| 		return "", err | ||||
| 	} | ||||
| @@ -380,6 +398,19 @@ func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*d | ||||
| 		err error | ||||
| 	) | ||||
|  | ||||
| 	// If avatar is unset, check if UseLocalAvatar contains the message's | ||||
| 	// account or protocol, and if so, try to find a local avatar | ||||
| 	if msg.Avatar == "" { | ||||
| 		for _, val := range b.GetStringSlice("UseLocalAvatar") { | ||||
| 			if msg.Protocol == val || msg.Account == val { | ||||
| 				if avatar := b.findAvatar(msg); avatar != "" { | ||||
| 					msg.Avatar = avatar | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// WebhookParams can have either `Content` or `File`. | ||||
|  | ||||
| 	// We can't send empty messages. | ||||
| @@ -429,3 +460,11 @@ func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*d | ||||
| 	} | ||||
| 	return res, err | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) findAvatar(m *config.Message) string { | ||||
| 	member, err := b.getGuildMemberByNick(m.Username) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return member.User.AvatarURL("") | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package bdiscord | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/bwmarrin/discordgo" | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| ) | ||||
|  | ||||
| func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam | ||||
| @@ -36,6 +36,11 @@ func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Ignore our own typing messages | ||||
| 	if m.UserID == b.userID { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping} | ||||
| 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||
| 	b.Remote <- rmsg | ||||
| @@ -90,12 +95,12 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 	// set channel name | ||||
| 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||
|  | ||||
| 	// set username | ||||
| 	if !b.GetBool("UseUserName") { | ||||
| 	fromWebhook := m.WebhookID != "" | ||||
| 	if !fromWebhook && !b.GetBool("UseUserName") { | ||||
| 		rmsg.Username = b.getNick(m.Author, m.GuildID) | ||||
| 	} else { | ||||
| 		rmsg.Username = m.Author.Username | ||||
| 		if b.GetBool("UseDiscriminator") { | ||||
| 		if !fromWebhook && b.GetBool("UseDiscriminator") { | ||||
| 			rmsg.Username += "#" + m.Author.Discriminator | ||||
| 		} | ||||
| 	} | ||||
| @@ -103,7 +108,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 	// if we have embedded content add it to text | ||||
| 	if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil { | ||||
| 		for _, embed := range m.Message.Embeds { | ||||
| 			rmsg.Text = rmsg.Text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n" | ||||
| 			rmsg.Text += handleEmbed(embed) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -119,6 +124,9 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 		rmsg.Event = config.EventUserAction | ||||
| 	} | ||||
|  | ||||
| 	// Replace emotes | ||||
| 	rmsg.Text = replaceEmotes(rmsg.Text) | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 	b.Remote <- rmsg | ||||
| @@ -192,3 +200,33 @@ func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRe | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|  | ||||
| func handleEmbed(embed *discordgo.MessageEmbed) string { | ||||
| 	var t []string | ||||
| 	var result string | ||||
|  | ||||
| 	t = append(t, embed.Title) | ||||
| 	t = append(t, embed.Description) | ||||
| 	t = append(t, embed.URL) | ||||
|  | ||||
| 	i := 0 | ||||
| 	for _, e := range t { | ||||
| 		if e == "" { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		i++ | ||||
| 		if i == 1 { | ||||
| 			result += " embed: " + e | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		result += " - " + e | ||||
| 	} | ||||
|  | ||||
| 	if result != "" { | ||||
| 		result += "\n" | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
|   | ||||
							
								
								
									
										58
									
								
								bridge/discord/handlers_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								bridge/discord/handlers_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| package bdiscord | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestHandleEmbed(t *testing.T) { | ||||
| 	testcases := map[string]struct { | ||||
| 		embed  *discordgo.MessageEmbed | ||||
| 		result string | ||||
| 	}{ | ||||
| 		"allempty": { | ||||
| 			embed:  &discordgo.MessageEmbed{}, | ||||
| 			result: "", | ||||
| 		}, | ||||
| 		"one": { | ||||
| 			embed: &discordgo.MessageEmbed{ | ||||
| 				Title: "blah", | ||||
| 			}, | ||||
| 			result: " embed: blah\n", | ||||
| 		}, | ||||
| 		"two": { | ||||
| 			embed: &discordgo.MessageEmbed{ | ||||
| 				Title:       "blah", | ||||
| 				Description: "blah2", | ||||
| 			}, | ||||
| 			result: " embed: blah - blah2\n", | ||||
| 		}, | ||||
| 		"three": { | ||||
| 			embed: &discordgo.MessageEmbed{ | ||||
| 				Title:       "blah", | ||||
| 				Description: "blah2", | ||||
| 				URL:         "blah3", | ||||
| 			}, | ||||
| 			result: " embed: blah - blah2 - blah3\n", | ||||
| 		}, | ||||
| 		"twob": { | ||||
| 			embed: &discordgo.MessageEmbed{ | ||||
| 				Description: "blah2", | ||||
| 				URL:         "blah3", | ||||
| 			}, | ||||
| 			result: " embed: blah2 - blah3\n", | ||||
| 		}, | ||||
| 		"oneb": { | ||||
| 			embed: &discordgo.MessageEmbed{ | ||||
| 				URL: "blah3", | ||||
| 			}, | ||||
| 			result: " embed: blah3\n", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for name, tc := range testcases { | ||||
| 		assert.Equalf(t, tc.result, handleEmbed(tc.embed), "Testcases %s", name) | ||||
| 	} | ||||
| } | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
|  | ||||
| 	"github.com/bwmarrin/discordgo" | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| ) | ||||
|  | ||||
| func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string { | ||||
| @@ -137,6 +137,7 @@ var ( | ||||
| 	// See https://discordapp.com/developers/docs/reference#message-formatting. | ||||
| 	channelMentionRE = regexp.MustCompile("<#[0-9]+>") | ||||
| 	userMentionRE    = regexp.MustCompile("@[^@\n]{1,32}") | ||||
| 	emoteRE          = regexp.MustCompile(`<a?(:\w+:)\d+>`) | ||||
| ) | ||||
|  | ||||
| func (b *Bdiscord) replaceChannelMentions(text string) string { | ||||
| @@ -182,9 +183,14 @@ func (b *Bdiscord) replaceUserMentions(text string) string { | ||||
| 	return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc) | ||||
| } | ||||
|  | ||||
| func replaceEmotes(text string) string { | ||||
| 	return emoteRE.ReplaceAllString(text, "$1") | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) replaceAction(text string) (string, bool) { | ||||
| 	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") { | ||||
| 		return text[1 : len(text)-1], true | ||||
| 	length := len(text) | ||||
| 	if length > 1 && text[0] == '_' && text[length-1] == '_' { | ||||
| 		return text[1 : length-1], true | ||||
| 	} | ||||
| 	return text, false | ||||
| } | ||||
| @@ -203,6 +209,40 @@ func (b *Bdiscord) splitURL(url string) (string, string) { | ||||
| 	return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken] | ||||
| } | ||||
|  | ||||
| // getcacheID tries to find a corresponding msgID in the webhook cache. | ||||
| // if not found returns the original request. | ||||
| func (b *Bdiscord) getCacheID(msgID string) string { | ||||
| 	b.webhookMutex.RLock() | ||||
| 	defer b.webhookMutex.RUnlock() | ||||
| 	for k, v := range b.webhookCache { | ||||
| 		if msgID == k { | ||||
| 			return v | ||||
| 		} | ||||
| 	} | ||||
| 	return msgID | ||||
| } | ||||
|  | ||||
| // updateCacheID updates the cache so that the newID takes the place of | ||||
| // the original ID. This is used for edit/deletes in combination with webhooks | ||||
| // as editing a message via webhook means deleting the message and creating a | ||||
| // new message (with a new ID). This ID needs to be set instead of the original ID | ||||
| func (b *Bdiscord) updateCacheID(origID, newID string) { | ||||
| 	b.webhookMutex.Lock() | ||||
| 	match := false | ||||
| 	for k, v := range b.webhookCache { | ||||
| 		if v == origID { | ||||
| 			delete(b.webhookCache, k) | ||||
| 			b.webhookCache[origID] = newID | ||||
| 			match = true | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| 	if !match && origID != "" { | ||||
| 		b.webhookCache[origID] = newID | ||||
| 	} | ||||
| 	b.webhookMutex.Unlock() | ||||
| } | ||||
|  | ||||
| func enumerateUsernames(s string) []string { | ||||
| 	onlySpace := true | ||||
| 	for _, r := range s { | ||||
|   | ||||
| @@ -180,7 +180,7 @@ func ClipMessage(text string, length int) string { | ||||
|  | ||||
| // ParseMarkdown takes in an input string as markdown and parses it to html | ||||
| func ParseMarkdown(input string) string { | ||||
| 	extensions := parser.HardLineBreak | ||||
| 	extensions := parser.HardLineBreak | parser.NoIntraEmphasis | ||||
| 	markdownParser := parser.NewWithExtensions(extensions) | ||||
| 	renderer := html.NewRenderer(html.RendererOptions{ | ||||
| 		Flags: 0, | ||||
|   | ||||
| @@ -10,8 +10,8 @@ import ( | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/dfordsoft/golib/ic" | ||||
| 	"github.com/lrstanley/girc" | ||||
| 	"github.com/missdeer/golib/ic" | ||||
| 	"github.com/paulrosania/go-charset/charset" | ||||
| 	"github.com/saintfish/chardet" | ||||
|  | ||||
| @@ -54,12 +54,12 @@ func (b *Birc) handleFiles(msg *config.Message) bool { | ||||
| 	for _, f := range msg.Extra["file"] { | ||||
| 		fi := f.(config.FileInfo) | ||||
| 		if fi.Comment != "" { | ||||
| 			msg.Text += fi.Comment + ": " | ||||
| 			msg.Text += fi.Comment + " : " | ||||
| 		} | ||||
| 		if fi.URL != "" { | ||||
| 			msg.Text = fi.URL | ||||
| 			if fi.Comment != "" { | ||||
| 				msg.Text = fi.Comment + ": " + fi.URL | ||||
| 				msg.Text = fi.Comment + " : " + fi.URL | ||||
| 			} | ||||
| 		} | ||||
| 		b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event} | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/lrstanley/girc" | ||||
| 	stripmd "github.com/writeas/go-strip-markdown" | ||||
|  | ||||
| 	// We need to import the 'data' package as an implicit dependency. | ||||
| 	// See: https://godoc.org/github.com/paulrosania/go-charset/charset | ||||
| @@ -156,6 +157,10 @@ func (b *Birc) Send(msg config.Message) (string, error) { | ||||
| 	} | ||||
|  | ||||
| 	var msgLines []string | ||||
| 	if b.GetBool("StripMarkdown") { | ||||
| 		msg.Text = stripmd.Strip(msg.Text) | ||||
| 	} | ||||
|  | ||||
| 	if b.GetBool("MessageSplit") { | ||||
| 		msgLines = helper.GetSubLines(msg.Text, b.MessageLength) | ||||
| 	} else { | ||||
| @@ -167,12 +172,8 @@ func (b *Birc) Send(msg config.Message) (string, error) { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		b.Local <- config.Message{ | ||||
| 			Text:     msgLines[i], | ||||
| 			Username: msg.Username, | ||||
| 			Channel:  msg.Channel, | ||||
| 			Event:    msg.Event, | ||||
| 		} | ||||
| 		msg.Text = msgLines[i] | ||||
| 		b.Local <- msg | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat" | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" | ||||
| ) | ||||
|  | ||||
| func (b *Bkeybase) handleKeybase() { | ||||
| @@ -20,7 +20,7 @@ func (b *Bkeybase) handleKeybase() { | ||||
| 				b.Log.Errorf("failed to read message: %s", err.Error()) | ||||
| 			} | ||||
|  | ||||
| 			if msg.Message.Content.Type != "text" { | ||||
| 			if msg.Message.Content.TypeName != "text" { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| @@ -34,7 +34,7 @@ func (b *Bkeybase) handleKeybase() { | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| func (b *Bkeybase) handleMessage(msg kbchat.Message) { | ||||
| func (b *Bkeybase) handleMessage(msg chat1.MsgSummary) { | ||||
| 	b.Log.Debugf("== Receiving event: %#v", msg) | ||||
| 	if msg.Channel.TopicName != b.channel || msg.Channel.Name != b.team { | ||||
| 		return | ||||
| @@ -45,10 +45,10 @@ func (b *Bkeybase) handleMessage(msg kbchat.Message) { | ||||
| 		// TODO download avatar | ||||
|  | ||||
| 		// Create our message | ||||
| 		rmsg := config.Message{Username: msg.Sender.Username, Text: msg.Content.Text.Body, UserID: msg.Sender.Uid, Channel: msg.Channel.TopicName, ID: strconv.Itoa(msg.MsgID), Account: b.Account} | ||||
| 		rmsg := config.Message{Username: msg.Sender.Username, Text: msg.Content.Text.Body, UserID: string(msg.Sender.Uid), Channel: msg.Channel.TopicName, ID: strconv.Itoa(int(msg.Id)), Account: b.Account} | ||||
|  | ||||
| 		// Text must be a string | ||||
| 		if msg.Content.Type != "text" { | ||||
| 		if msg.Content.TypeName != "text" { | ||||
| 			b.Log.Errorf("message is not text") | ||||
| 			return | ||||
| 		} | ||||
|   | ||||
| @@ -90,16 +90,17 @@ func (b *Bkeybase) Send(msg config.Message) (string, error) { | ||||
| 				return "", err | ||||
| 			} | ||||
|  | ||||
| 			_, _ = b.kbc.SendAttachmentByTeam(b.team, fpath, fcaption, &b.channel) | ||||
| 			_, _ = b.kbc.SendAttachmentByTeam(b.team, &b.channel, fpath, fcaption) | ||||
| 		} | ||||
|  | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	// Send regular message | ||||
| 	resp, err := b.kbc.SendMessageByTeamName(b.team, msg.Username+msg.Text, &b.channel) | ||||
| 	text := msg.Username + msg.Text | ||||
| 	resp, err := b.kbc.SendMessageByTeamName(b.team, &b.channel, text) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return strconv.Itoa(resp.Result.MsgID), err | ||||
| 	return strconv.Itoa(int(*resp.Result.MessageID)), err | ||||
| } | ||||
|   | ||||
| @@ -124,6 +124,14 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) { | ||||
| 		return resp.EventID, err | ||||
| 	} | ||||
|  | ||||
| 	if b.GetBool("HTMLDisable") { | ||||
| 		resp, err := b.mc.SendText(channel, msg.Username+msg.Text) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		return resp.EventID, err | ||||
| 	} | ||||
|  | ||||
| 	username := html.EscapeString(msg.Username) | ||||
| 	// check if we have a </tag>. if we have, we don't escape HTML. #696 | ||||
| 	if b.htmlTag.MatchString(msg.Username) { | ||||
| @@ -172,10 +180,15 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// TODO download avatar | ||||
|  | ||||
| 		// Create our message | ||||
| 		rmsg := config.Message{Username: ev.Sender[1:], Channel: channel, Account: b.Account, UserID: ev.Sender, ID: ev.ID} | ||||
| 		rmsg := config.Message{ | ||||
| 			Username: ev.Sender[1:], | ||||
| 			Channel:  channel, | ||||
| 			Account:  b.Account, | ||||
| 			UserID:   ev.Sender, | ||||
| 			ID:       ev.ID, | ||||
| 			Avatar:   b.getAvatarURL(ev.Sender), | ||||
| 		} | ||||
|  | ||||
| 		// Text must be a string | ||||
| 		if rmsg.Text, ok = ev.Content["body"].(string); !ok { | ||||
| @@ -358,3 +371,17 @@ func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool { | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // getAvatarURL returns the avatar URL of the specified sender | ||||
| func (b *Bmatrix) getAvatarURL(sender string) string { | ||||
| 	mxcURL, err := b.mc.GetSenderAvatarURL(sender) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("getAvatarURL failed: %s", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	url := strings.ReplaceAll(mxcURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/") | ||||
| 	if url != "" { | ||||
| 		url += "?width=37&height=37&method=crop" | ||||
| 	} | ||||
| 	return url | ||||
| } | ||||
|   | ||||
							
								
								
									
										101
									
								
								bridge/msteams/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								bridge/msteams/handler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| package bmsteams | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
|  | ||||
| 	msgraph "github.com/yaegashi/msgraph.go/beta" | ||||
| ) | ||||
|  | ||||
| func (b *Bmsteams) findFile(weburl string) (string, error) { | ||||
| 	itemRB, err := b.gc.GetDriveItemByURL(b.ctx, weburl) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	itemRB.Workbook().Worksheets() | ||||
| 	b.gc.Workbooks() | ||||
| 	item, err := itemRB.Request().Get(b.ctx) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if url, ok := item.GetAdditionalData("@microsoft.graph.downloadUrl"); ok { | ||||
| 		return url.(string), nil | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| // handleDownloadFile handles file download | ||||
| func (b *Bmsteams) handleDownloadFile(rmsg *config.Message, filename, weburl string) error { | ||||
| 	realURL, err := b.findFile(weburl) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Actually download the file. | ||||
| 	data, err := helper.DownloadFile(realURL) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("download %s failed %#v", weburl, err) | ||||
| 	} | ||||
|  | ||||
| 	// If a comment is attached to the file(s) it is in the 'Text' field of the teams messge event | ||||
| 	// and should be added as comment to only one of the files. We reset the 'Text' field to ensure | ||||
| 	// that the comment is not duplicated. | ||||
| 	comment := rmsg.Text | ||||
| 	rmsg.Text = "" | ||||
| 	helper.HandleDownloadData(b.Log, rmsg, filename, comment, weburl, data, b.General) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) handleAttachments(rmsg *config.Message, msg msgraph.ChatMessage) { | ||||
| 	for _, a := range msg.Attachments { | ||||
| 		//remove the attachment tags from the text | ||||
| 		rmsg.Text = attachRE.ReplaceAllString(rmsg.Text, "") | ||||
|  | ||||
| 		//handle a code snippet (code block) | ||||
| 		if *a.ContentType == "application/vnd.microsoft.card.codesnippet" { | ||||
| 			b.handleCodeSnippet(rmsg, a) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		//handle the download | ||||
| 		err := b.handleDownloadFile(rmsg, *a.Name, *a.ContentURL) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("download of %s failed: %s", *a.Name, err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type AttachContent struct { | ||||
| 	Language       string `json:"language"` | ||||
| 	CodeSnippetURL string `json:"codeSnippetUrl"` | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) handleCodeSnippet(rmsg *config.Message, attach msgraph.ChatMessageAttachment) { | ||||
| 	var content AttachContent | ||||
| 	err := json.Unmarshal([]byte(*attach.Content), &content) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("unmarshal codesnippet failed: %s", err) | ||||
| 		return | ||||
| 	} | ||||
| 	s := strings.Split(content.CodeSnippetURL, "/") | ||||
| 	if len(s) != 13 { | ||||
| 		b.Log.Errorf("codesnippetUrl has unexpected size: %s", content.CodeSnippetURL) | ||||
| 		return | ||||
| 	} | ||||
| 	resp, err := b.gc.Teams().Request().Client().Get(content.CodeSnippetURL) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("retrieving snippet content failed:%s", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	res, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("reading snippet data failed: %s", err) | ||||
| 		return | ||||
| 	} | ||||
| 	rmsg.Text = rmsg.Text + "\n```" + content.Language + "\n" + string(res) + "\n```\n" | ||||
| } | ||||
							
								
								
									
										224
									
								
								bridge/msteams/msteams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								bridge/msteams/msteams.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | ||||
| package bmsteams | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
|  | ||||
| 	"github.com/mattn/godown" | ||||
| 	msgraph "github.com/yaegashi/msgraph.go/beta" | ||||
| 	"github.com/yaegashi/msgraph.go/msauth" | ||||
|  | ||||
| 	"golang.org/x/oauth2" | ||||
| ) | ||||
|  | ||||
| var defaultScopes = []string{"openid", "profile", "offline_access", "Group.Read.All", "Group.ReadWrite.All"} | ||||
| var attachRE = regexp.MustCompile(`<attachment id=.*?attachment>`) | ||||
|  | ||||
| type Bmsteams struct { | ||||
| 	gc    *msgraph.GraphServiceRequestBuilder | ||||
| 	ctx   context.Context | ||||
| 	botID string | ||||
| 	*bridge.Config | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	return &Bmsteams{Config: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) Connect() error { | ||||
| 	tokenCachePath := b.GetString("sessionFile") | ||||
| 	if tokenCachePath == "" { | ||||
| 		tokenCachePath = "msteams_session.json" | ||||
| 	} | ||||
| 	ctx := context.Background() | ||||
| 	m := msauth.NewManager() | ||||
| 	m.LoadFile(tokenCachePath) //nolint:errcheck | ||||
| 	ts, err := m.DeviceAuthorizationGrant(ctx, b.GetString("TenantID"), b.GetString("ClientID"), defaultScopes, nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = m.SaveFile(tokenCachePath) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Couldn't save sessionfile in %s: %s", tokenCachePath, err) | ||||
| 	} | ||||
| 	// make file readable only for matterbridge user | ||||
| 	err = os.Chmod(tokenCachePath, 0600) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Couldn't change permissions for %s: %s", tokenCachePath, err) | ||||
| 	} | ||||
| 	httpClient := oauth2.NewClient(ctx, ts) | ||||
| 	graphClient := msgraph.NewClient(httpClient) | ||||
| 	b.gc = graphClient | ||||
| 	b.ctx = ctx | ||||
|  | ||||
| 	err = b.setBotID() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b.Log.Info("Connection succeeded") | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) Disconnect() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	go func(name string) { | ||||
| 		for { | ||||
| 			err := b.poll(name) | ||||
| 			if err != nil { | ||||
| 				b.Log.Errorf("polling failed for %s: %s. retrying in 5 seconds", name, err) | ||||
| 			} | ||||
| 			time.Sleep(time.Second * 5) | ||||
| 		} | ||||
| 	}(channel.Name) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) Send(msg config.Message) (string, error) { | ||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | ||||
| 	if msg.ParentID != "" && msg.ParentID != "msg-parent-not-found" { | ||||
| 		return b.sendReply(msg) | ||||
| 	} | ||||
| 	if msg.ParentID == "msg-parent-not-found" { | ||||
| 		msg.ParentID = "" | ||||
| 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||
| 	} | ||||
| 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().Request() | ||||
| 	text := msg.Username + msg.Text | ||||
| 	content := &msgraph.ItemBody{Content: &text} | ||||
| 	rmsg := &msgraph.ChatMessage{Body: content} | ||||
| 	res, err := ct.Add(b.ctx, rmsg) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return *res.ID, nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) sendReply(msg config.Message) (string, error) { | ||||
| 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().ID(msg.ParentID).Replies().Request() | ||||
| 	// Handle prefix hint for unthreaded messages. | ||||
|  | ||||
| 	text := msg.Username + msg.Text | ||||
| 	content := &msgraph.ItemBody{Content: &text} | ||||
| 	rmsg := &msgraph.ChatMessage{Body: content} | ||||
| 	res, err := ct.Add(b.ctx, rmsg) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return *res.ID, nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) { | ||||
| 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(channel).Messages().Request() | ||||
| 	rct, err := ct.Get(b.ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	b.Log.Debugf("got %#v messages", len(rct)) | ||||
| 	return rct, nil | ||||
| } | ||||
|  | ||||
| //nolint:gocognit | ||||
| func (b *Bmsteams) poll(channelName string) error { | ||||
| 	msgmap := make(map[string]time.Time) | ||||
| 	b.Log.Debug("getting initial messages") | ||||
| 	res, err := b.getMessages(channelName) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, msg := range res { | ||||
| 		msgmap[*msg.ID] = *msg.CreatedDateTime | ||||
| 		if msg.LastModifiedDateTime != nil { | ||||
| 			msgmap[*msg.ID] = *msg.LastModifiedDateTime | ||||
| 		} | ||||
| 	} | ||||
| 	time.Sleep(time.Second * 5) | ||||
| 	b.Log.Debug("polling for messages") | ||||
| 	for { | ||||
| 		res, err := b.getMessages(channelName) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		for i := len(res) - 1; i >= 0; i-- { | ||||
| 			msg := res[i] | ||||
| 			if mtime, ok := msgmap[*msg.ID]; ok { | ||||
| 				if mtime == *msg.CreatedDateTime && msg.LastModifiedDateTime == nil { | ||||
| 					continue | ||||
| 				} | ||||
| 				if msg.LastModifiedDateTime != nil && mtime == *msg.LastModifiedDateTime { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if b.GetBool("debug") { | ||||
| 				b.Log.Debug("Msg dump: ", spew.Sdump(msg)) | ||||
| 			} | ||||
|  | ||||
| 			// skip non-user message for now. | ||||
| 			if msg.From.User == nil { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			if *msg.From.User.ID == b.botID { | ||||
| 				b.Log.Debug("skipping own message") | ||||
| 				msgmap[*msg.ID] = *msg.CreatedDateTime | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			msgmap[*msg.ID] = *msg.CreatedDateTime | ||||
| 			if msg.LastModifiedDateTime != nil { | ||||
| 				msgmap[*msg.ID] = *msg.LastModifiedDateTime | ||||
| 			} | ||||
| 			b.Log.Debugf("<= Sending message from %s on %s to gateway", *msg.From.User.DisplayName, b.Account) | ||||
| 			text := b.convertToMD(*msg.Body.Content) | ||||
| 			rmsg := config.Message{ | ||||
| 				Username: *msg.From.User.DisplayName, | ||||
| 				Text:     text, | ||||
| 				Channel:  channelName, | ||||
| 				Account:  b.Account, | ||||
| 				Avatar:   "", | ||||
| 				UserID:   *msg.From.User.ID, | ||||
| 				ID:       *msg.ID, | ||||
| 				Extra:    make(map[string][]interface{}), | ||||
| 			} | ||||
|  | ||||
| 			b.handleAttachments(&rmsg, msg) | ||||
| 			b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 			b.Remote <- rmsg | ||||
| 		} | ||||
| 		time.Sleep(time.Second * 5) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) setBotID() error { | ||||
| 	req := b.gc.Me().Request() | ||||
| 	r, err := req.Get(b.ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b.botID = *r.ID | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmsteams) convertToMD(text string) string { | ||||
| 	if !strings.Contains(text, "<div>") { | ||||
| 		return text | ||||
| 	} | ||||
| 	var sb strings.Builder | ||||
| 	err := godown.Convert(&sb, strings.NewReader(text), nil) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Couldn't convert message to markdown %s", text) | ||||
| 		return text | ||||
| 	} | ||||
| 	return sb.String() | ||||
| } | ||||
| @@ -2,6 +2,7 @@ package brocketchat | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/matterbridge/Rocket.Chat.Go.SDK/models" | ||||
| ) | ||||
|  | ||||
| func (b *Brocketchat) handleRocket() { | ||||
| @@ -38,6 +39,23 @@ func (b *Brocketchat) handleRocketHook(messages chan *config.Message) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Brocketchat) handleStatusEvent(ev models.Message, rmsg *config.Message) bool { | ||||
| 	switch ev.Type { | ||||
| 	case "": | ||||
| 		// this is a normal message, no processing needed | ||||
| 		// return true so the message is not dropped | ||||
| 		return true | ||||
| 	case sUserJoined, sUserLeft: | ||||
| 		rmsg.Event = config.EventJoinLeave | ||||
| 		return true | ||||
| 	case sRoomChangedTopic: | ||||
| 		rmsg.Event = config.EventTopicChange | ||||
| 		return true | ||||
| 	} | ||||
| 	b.Log.Debugf("Dropping message with unknown type: %s", ev.Type) | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | ||||
| 	for message := range b.messageChan { | ||||
| 		// skip messages with same ID, apparently messages get duplicated for an unknown reason | ||||
| @@ -59,7 +77,12 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | ||||
| 			UserID:   message.User.ID, | ||||
| 			ID:       message.ID, | ||||
| 		} | ||||
| 		messages <- rmsg | ||||
|  | ||||
| 		// handleStatusEvent returns false if the message should be dropped | ||||
| 		// in that case it is probably some modification to the channel we do not want to relay | ||||
| 		if b.handleStatusEvent(m, rmsg) { | ||||
| 			messages <- rmsg | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,12 @@ type Brocketchat struct { | ||||
| 	sync.RWMutex | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	sUserJoined       = "uj" | ||||
| 	sUserLeft         = "ul" | ||||
| 	sRoomChangedTopic = "room_changed_topic" | ||||
| ) | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	newCache, err := lru.New(100) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -1,18 +1,22 @@ | ||||
| package bslack | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"html" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/nlopes/slack" | ||||
| 	"github.com/slack-go/slack" | ||||
| ) | ||||
|  | ||||
| // ErrEventIgnored is for events that should be ignored | ||||
| var ErrEventIgnored = errors.New("this event message should ignored") | ||||
|  | ||||
| func (b *Bslack) handleSlack() { | ||||
| 	messages := make(chan *config.Message) | ||||
| 	if b.GetString(incomingWebhookConfig) != "" { | ||||
| 	if b.GetString(incomingWebhookConfig) != "" && b.GetString(tokenConfig) == "" { | ||||
| 		b.Log.Debugf("Choosing webhooks based receiving") | ||||
| 		go b.handleMatterHook(messages) | ||||
| 	} else { | ||||
| @@ -53,7 +57,9 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) { | ||||
| 				continue | ||||
| 			} | ||||
| 			rmsg, err := b.handleTypingEvent(ev) | ||||
| 			if err != nil { | ||||
| 			if err == ErrEventIgnored { | ||||
| 				continue | ||||
| 			} else if err != nil { | ||||
| 				b.Log.Errorf("%#v", err) | ||||
| 				continue | ||||
| 			} | ||||
| @@ -87,7 +93,7 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) { | ||||
| 			b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj) | ||||
| 		case *slack.MemberJoinedChannelEvent: | ||||
| 			b.users.populateUser(ev.User) | ||||
| 		case *slack.HelloEvent, *slack.LatencyReport: | ||||
| 		case *slack.HelloEvent, *slack.LatencyReport, *slack.ConnectingEvent: | ||||
| 			continue | ||||
| 		default: | ||||
| 			b.Log.Debugf("Unhandled incoming event: %T", ev) | ||||
| @@ -124,11 +130,11 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Skip any messages that we made ourselves or from 'slackbot' (see #527). | ||||
| 	if ev.Username == sSlackBotUser || | ||||
| 		(b.rtm != nil && ev.Username == b.si.User.Name) || | ||||
| 		(len(ev.Attachments) > 0 && ev.Attachments[0].CallbackID == "matterbridge_"+b.uuid) { | ||||
| 		return true | ||||
| 	// Check for our callback ID | ||||
| 	hasOurCallbackID := false | ||||
| 	if len(ev.Blocks.BlockSet) == 1 { | ||||
| 		block, ok := ev.Blocks.BlockSet[0].(*slack.SectionBlock) | ||||
| 		hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid | ||||
| 	} | ||||
|  | ||||
| 	if ev.SubMessage != nil { | ||||
| @@ -143,6 +149,16 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool { | ||||
| 		if ev.SubType == "message_replied" && ev.Hidden { | ||||
| 			return true | ||||
| 		} | ||||
| 		if len(ev.SubMessage.Blocks.BlockSet) == 1 { | ||||
| 			block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock) | ||||
| 			hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Skip any messages that we made ourselves or from 'slackbot' (see #527). | ||||
| 	if ev.Username == sSlackBotUser || | ||||
| 		(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	if len(ev.Files) > 0 { | ||||
| @@ -270,6 +286,9 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) | ||||
| } | ||||
|  | ||||
| func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) { | ||||
| 	if ev.User == b.si.User.ID { | ||||
| 		return nil, ErrEventIgnored | ||||
| 	} | ||||
| 	channelInfo, err := b.channels.getChannelByID(ev.Channel) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|   | ||||
| @@ -7,8 +7,8 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/nlopes/slack" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/slack-go/slack" | ||||
| ) | ||||
|  | ||||
| // populateReceivedMessage shapes the initial Matterbridge message that we will forward to the | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/matterhook" | ||||
| 	"github.com/nlopes/slack" | ||||
| 	"github.com/slack-go/slack" | ||||
| ) | ||||
|  | ||||
| type BLegacy struct { | ||||
|   | ||||
| @@ -13,8 +13,8 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/42wim/matterbridge/matterhook" | ||||
| 	lru "github.com/hashicorp/golang-lru" | ||||
| 	"github.com/nlopes/slack" | ||||
| 	"github.com/rs/xid" | ||||
| 	"github.com/slack-go/slack" | ||||
| ) | ||||
|  | ||||
| type Bslack struct { | ||||
| @@ -64,6 +64,7 @@ const ( | ||||
| 	editSuffixConfig      = "EditSuffix" | ||||
| 	iconURLConfig         = "iconurl" | ||||
| 	noSendJoinConfig      = "nosendjoinpart" | ||||
| 	messageLength         = 3000 | ||||
| ) | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| @@ -194,6 +195,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) { | ||||
| 		b.Log.Debugf("=> Receiving %#v", msg) | ||||
| 	} | ||||
|  | ||||
| 	msg.Text = helper.ClipMessage(msg.Text, messageLength) | ||||
| 	msg.Text = b.replaceCodeFence(msg.Text) | ||||
|  | ||||
| 	// Make a action /me of the message | ||||
| @@ -202,7 +204,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) { | ||||
| 	} | ||||
|  | ||||
| 	// Use webhook to send the message | ||||
| 	if b.GetString(outgoingWebhookConfig) != "" { | ||||
| 	if b.GetString(outgoingWebhookConfig) != "" && b.GetString(tokenConfig) == "" { | ||||
| 		return "", b.sendWebhook(msg) | ||||
| 	} | ||||
| 	return b.sendRTM(msg) | ||||
| @@ -408,7 +410,6 @@ func (b *Bslack) editMessage(msg *config.Message, channelInfo *slack.Channel) (b | ||||
| 	} | ||||
| 	messageOptions := b.prepareMessageOptions(msg) | ||||
| 	for { | ||||
| 		messageOptions = append(messageOptions, slack.MsgOptionText(msg.Text, false)) | ||||
| 		_, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...) | ||||
| 		if err == nil { | ||||
| 			return true, nil | ||||
| @@ -427,11 +428,6 @@ func (b *Bslack) postMessage(msg *config.Message, channelInfo *slack.Channel) (s | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	messageOptions := b.prepareMessageOptions(msg) | ||||
| 	messageOptions = append( | ||||
| 		messageOptions, | ||||
| 		slack.MsgOptionText(msg.Text, false), | ||||
| 		slack.MsgOptionEnableLinkUnfurl(), | ||||
| 	) | ||||
| 	for { | ||||
| 		_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...) | ||||
| 		if err == nil { | ||||
| @@ -497,8 +493,6 @@ func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption { | ||||
| 	} | ||||
|  | ||||
| 	var attachments []slack.Attachment | ||||
| 	// add a callback ID so we can see we created it | ||||
| 	attachments = append(attachments, slack.Attachment{CallbackID: "matterbridge_" + b.uuid}) | ||||
| 	// add file attachments | ||||
| 	attachments = append(attachments, b.createAttach(msg.Extra)...) | ||||
| 	// add slack attachments (from another slack bridge) | ||||
| @@ -509,6 +503,19 @@ func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption { | ||||
| 	} | ||||
|  | ||||
| 	var opts []slack.MsgOption | ||||
| 	opts = append(opts, | ||||
| 		// provide regular text field (fallback used in Slack notifications, etc.) | ||||
| 		slack.MsgOptionText(msg.Text, false), | ||||
|  | ||||
| 		// add a callback ID so we can see we created it | ||||
| 		slack.MsgOptionBlocks(slack.NewSectionBlock( | ||||
| 			slack.NewTextBlockObject(slack.MarkdownType, msg.Text, false, false), | ||||
| 			nil, nil, | ||||
| 			slack.SectionBlockOptionBlockID("matterbridge_"+b.uuid), | ||||
| 		)), | ||||
|  | ||||
| 		slack.MsgOptionEnableLinkUnfurl(), | ||||
| 	) | ||||
| 	opts = append(opts, slack.MsgOptionAttachments(attachments...)) | ||||
| 	opts = append(opts, slack.MsgOptionPostMessageParameters(params)) | ||||
| 	return opts | ||||
|   | ||||
| @@ -8,8 +8,8 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/nlopes/slack" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| 	"github.com/slack-go/slack" | ||||
| ) | ||||
|  | ||||
| const minimumRefreshInterval = 10 * time.Second | ||||
|   | ||||
| @@ -39,22 +39,32 @@ func (b *Btelegram) handleGroups(rmsg *config.Message, message *tgbotapi.Message | ||||
|  | ||||
| // handleForwarded handles forwarded messages | ||||
| func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Message) { | ||||
| 	if message.ForwardFrom != nil { | ||||
| 		usernameForward := "" | ||||
| 		if b.GetBool("UseFirstName") { | ||||
| 	if message.ForwardDate == 0 { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if message.ForwardFrom == nil { | ||||
| 		rmsg.Text = "Forwarded from " + unknownUser + ": " + rmsg.Text | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	usernameForward := "" | ||||
| 	if b.GetBool("UseFirstName") { | ||||
| 		usernameForward = message.ForwardFrom.FirstName | ||||
| 	} | ||||
|  | ||||
| 	if usernameForward == "" { | ||||
| 		usernameForward = message.ForwardFrom.UserName | ||||
| 		if usernameForward == "" { | ||||
| 			usernameForward = message.ForwardFrom.FirstName | ||||
| 		} | ||||
| 		if usernameForward == "" { | ||||
| 			usernameForward = message.ForwardFrom.UserName | ||||
| 			if usernameForward == "" { | ||||
| 				usernameForward = message.ForwardFrom.FirstName | ||||
| 			} | ||||
| 		} | ||||
| 		if usernameForward == "" { | ||||
| 			usernameForward = unknownUser | ||||
| 		} | ||||
| 		rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text | ||||
| 	} | ||||
|  | ||||
| 	if usernameForward == "" { | ||||
| 		usernameForward = unknownUser | ||||
| 	} | ||||
|  | ||||
| 	rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text | ||||
| } | ||||
|  | ||||
| // handleQuoting handles quoting of previous messages | ||||
| @@ -95,7 +105,7 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa | ||||
| 			} | ||||
| 		} | ||||
| 		// only download avatars if we have a place to upload them (configured mediaserver) | ||||
| 		if b.General.MediaServerUpload != "" { | ||||
| 		if b.General.MediaServerUpload != "" || (b.General.MediaServerDownload != "" && b.General.MediaDownloadPath != "") { | ||||
| 			b.handleDownloadAvatar(message.From.ID, rmsg.Channel) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/Rhymen/go-whatsapp" | ||||
| 	"github.com/jpillora/backoff" | ||||
| ) | ||||
|  | ||||
| /* | ||||
| @@ -22,10 +23,43 @@ Check: | ||||
| // HandleError received from WhatsApp | ||||
| func (b *Bwhatsapp) HandleError(err error) { | ||||
| 	// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843 | ||||
| 	if strings.Contains(err.Error(), "error processing data: received invalid data") { | ||||
| 	// ignore tag 174 errors. https://github.com/42wim/matterbridge/issues/1094 | ||||
| 	if strings.Contains(err.Error(), "error processing data: received invalid data") || strings.Contains(err.Error(), "invalid string with tag 174") { | ||||
| 		return | ||||
| 	} | ||||
| 	b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types | ||||
|  | ||||
| 	switch err.(type) { | ||||
| 	case *whatsapp.ErrConnectionClosed, *whatsapp.ErrConnectionFailed: | ||||
| 		b.reconnect(err) | ||||
| 	default: | ||||
| 		switch err { | ||||
| 		case whatsapp.ErrConnectionTimeout: | ||||
| 			b.reconnect(err) | ||||
| 		default: | ||||
| 			b.Log.Errorf("%v", err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bwhatsapp) reconnect(err error) { | ||||
| 	bf := &backoff.Backoff{ | ||||
| 		Min:    time.Second, | ||||
| 		Max:    5 * time.Minute, | ||||
| 		Jitter: true, | ||||
| 	} | ||||
| 	for { | ||||
| 		d := bf.Duration() | ||||
| 		b.Log.Errorf("Connection failed, underlying error: %v", err) | ||||
| 		b.Log.Infof("Waiting %s...", d) | ||||
| 		time.Sleep(d) | ||||
| 		b.Log.Info("Reconnecting...") | ||||
| 		err := b.conn.Restore() | ||||
| 		if err == nil { | ||||
| 			bf.Reset() | ||||
| 			b.startedAt = uint64(time.Now().Unix()) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // HandleTextMessage sent from WhatsApp, relay it to the brige | ||||
|   | ||||
| @@ -67,6 +67,7 @@ func (b *Bwhatsapp) Connect() error { | ||||
| 	// https://github.com/Rhymen/go-whatsapp#creating-a-connection | ||||
| 	b.Log.Debugln("Connecting to WhatsApp..") | ||||
| 	conn, err := whatsapp.NewConn(20 * time.Second) | ||||
| 	conn.SetClientVersion(0, 4, 2080) | ||||
| 	if err != nil { | ||||
| 		return errors.New("failed to connect to WhatsApp: " + err.Error()) | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										34
									
								
								bridge/xmpp/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								bridge/xmpp/handler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| package bxmpp | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/matterbridge/go-xmpp" | ||||
| ) | ||||
|  | ||||
| // handleDownloadAvatar downloads the avatar of userid from channel | ||||
| // sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful. | ||||
| // logs an error message if it fails | ||||
| func (b *Bxmpp) handleDownloadAvatar(avatar xmpp.AvatarData) { | ||||
| 	rmsg := config.Message{ | ||||
| 		Username: "system", | ||||
| 		Text:     "avatar", | ||||
| 		Channel:  b.parseChannel(avatar.From), | ||||
| 		Account:  b.Account, | ||||
| 		UserID:   avatar.From, | ||||
| 		Event:    config.EventAvatarDownload, | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 	} | ||||
| 	if _, ok := b.avatarMap[avatar.From]; !ok { | ||||
| 		b.Log.Debugf("Avatar.From: %s", avatar.From) | ||||
|  | ||||
| 		err := helper.HandleDownloadSize(b.Log, &rmsg, avatar.From+".png", int64(len(avatar.Data)), b.General) | ||||
| 		if err != nil { | ||||
| 			b.Log.Error(err) | ||||
| 			return | ||||
| 		} | ||||
| 		helper.HandleDownloadData(b.Log, &rmsg, avatar.From+".png", rmsg.Text, "", &avatar.Data, b.General) | ||||
| 		b.Log.Debugf("Avatar download complete") | ||||
| 		b.Remote <- rmsg | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										30
									
								
								bridge/xmpp/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								bridge/xmpp/helpers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| package bxmpp | ||||
|  | ||||
| import ( | ||||
| 	"regexp" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| ) | ||||
|  | ||||
| var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+") | ||||
|  | ||||
| // GetAvatar constructs a URL for a given user-avatar if it is available in the cache. | ||||
| func getAvatar(av map[string]string, userid string, general *config.Protocol) string { | ||||
| 	if hash, ok := av[userid]; ok { | ||||
| 		// NOTE: This does not happen in bridge/helper/helper.go but messes up XMPP | ||||
| 		id := pathRegex.ReplaceAllString(userid, "_") | ||||
| 		return general.MediaServerDownload + "/" + hash + "/" + id + ".png" | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) cacheAvatar(msg *config.Message) string { | ||||
| 	fi := msg.Extra["file"][0].(config.FileInfo) | ||||
| 	/* if we have a sha we have successfully uploaded the file to the media server, | ||||
| 	so we can now cache the sha */ | ||||
| 	if fi.SHA != "" { | ||||
| 		b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID) | ||||
| 		b.avatarMap[msg.UserID] = fi.SHA | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| @@ -23,12 +23,17 @@ type Bxmpp struct { | ||||
| 	xmppMap   map[string]string | ||||
| 	connected bool | ||||
| 	sync.RWMutex | ||||
|  | ||||
| 	avatarAvailability map[string]bool | ||||
| 	avatarMap          map[string]string | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	return &Bxmpp{ | ||||
| 		Config:  cfg, | ||||
| 		xmppMap: make(map[string]string), | ||||
| 		Config:             cfg, | ||||
| 		xmppMap:            make(map[string]string), | ||||
| 		avatarAvailability: make(map[string]bool), | ||||
| 		avatarMap:          make(map[string]string), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -67,8 +72,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) { | ||||
| 	if msg.Event == config.EventMsgDelete { | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | ||||
|  | ||||
| 	if msg.Event == config.EventAvatarDownload { | ||||
| 		return b.cacheAvatar(&msg), nil | ||||
| 	} | ||||
|  | ||||
| 	// Make a action /me of the message, prepend the username with it. | ||||
| 	// https://xmpp.org/extensions/xep-0245.html | ||||
| 	if msg.Event == config.EventUserAction { | ||||
| 		msg.Username = "/me " + msg.Username | ||||
| 	} | ||||
|  | ||||
| 	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support). | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||
| @@ -114,6 +130,9 @@ func (b *Bxmpp) createXMPP() error { | ||||
| 		ServerName:         strings.Split(b.GetString("Jid"), "@")[1], | ||||
| 		InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec | ||||
| 	} | ||||
|  | ||||
| 	xmpp.DebugWriter = b.Log.Writer() | ||||
|  | ||||
| 	options := xmpp.Options{ | ||||
| 		Host:                         b.GetString("Server"), | ||||
| 		User:                         b.GetString("Jid"), | ||||
| @@ -122,7 +141,6 @@ func (b *Bxmpp) createXMPP() error { | ||||
| 		StartTLS:                     true, | ||||
| 		TLSConfig:                    tc, | ||||
| 		Debug:                        b.GetBool("debug"), | ||||
| 		Logger:                       b.Log.Writer(), | ||||
| 		Session:                      true, | ||||
| 		Status:                       "", | ||||
| 		StatusMessage:                "", | ||||
| @@ -228,6 +246,16 @@ func (b *Bxmpp) handleXMPP() error { | ||||
| 					event = config.EventTopicChange | ||||
| 				} | ||||
|  | ||||
| 				available, sok := b.avatarAvailability[v.Remote] | ||||
| 				avatar := "" | ||||
| 				if !sok { | ||||
| 					b.Log.Debugf("Requesting avatar data") | ||||
| 					b.avatarAvailability[v.Remote] = false | ||||
| 					b.xc.AvatarRequestData(v.Remote) | ||||
| 				} else if available { | ||||
| 					avatar = getAvatar(b.avatarMap, v.Remote, b.General) | ||||
| 				} | ||||
|  | ||||
| 				msgID := v.ID | ||||
| 				if v.ReplaceID != "" { | ||||
| 					msgID = v.ReplaceID | ||||
| @@ -237,6 +265,7 @@ func (b *Bxmpp) handleXMPP() error { | ||||
| 					Text:     v.Text, | ||||
| 					Channel:  b.parseChannel(v.Remote), | ||||
| 					Account:  b.Account, | ||||
| 					Avatar:   avatar, | ||||
| 					UserID:   v.Remote, | ||||
| 					ID:       msgID, | ||||
| 					Event:    event, | ||||
| @@ -253,6 +282,10 @@ func (b *Bxmpp) handleXMPP() error { | ||||
| 				b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 				b.Remote <- rmsg | ||||
| 			} | ||||
| 		case xmpp.AvatarData: | ||||
| 			b.handleDownloadAvatar(v) | ||||
| 			b.avatarAvailability[v.From] = true | ||||
| 			b.Log.Debugf("Avatar for %s is now available", v.From) | ||||
| 		case xmpp.Presence: | ||||
| 			// Do nothing. | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										120
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,123 @@ | ||||
| # v1.17.5 | ||||
|  | ||||
| ## Enhancements | ||||
|  | ||||
| - irc: Add StripMarkdown option (irc). (#1145) | ||||
| - general: Increase debug logging with function,file and linenumber (#1147) | ||||
| - general: Update Dockerfile so inotify works (#1148) | ||||
| - matrix: Add an option to disable sending HTML to matrix. Fixes #1022 (#1135) | ||||
| - xmpp: Implement xep-0245 (xmpp). Closes #1137 (#1144) | ||||
|  | ||||
| ## Bugfix | ||||
|  | ||||
| - discord: Fix #1120: replaceAction "_" crash (discord) (#1121) | ||||
| - discord: Fix #1049: missing space before embeds (discord) (#1124) | ||||
| - discord: Fix webhook EventUserAction messages being skipped (discord) (#1133) | ||||
| - matrix: Avoid creating invalid url when the user doesn't have an avatar (matrix) (#1130) | ||||
| - msteams: Ignore non-user messages (msteams). Fixes #1141 (#1149) | ||||
| - slack: Do not use webhooks when token is configured (slack) (fixes #1123) (#1134) | ||||
| - telegram: Fix forward from hidden users (telegram). Closes #1131 (#1143) | ||||
| - xmpp: Prevent re-requesting avatar data (xmpp) (#1117) | ||||
|  | ||||
| This release couldn't exist without the following contributors: | ||||
| @qaisjp, @xnaas, @42wim, @Polynomdivision, @tfve | ||||
|  | ||||
| # v1.17.4 | ||||
|  | ||||
| ## Bugfix | ||||
|  | ||||
| - general: Lowercase account names. Fixes #1108 (#1110) | ||||
| - msteams: Remove panics and retry polling on failure (msteams). Fixes #1104 (#1105 | ||||
| - whatsapp: Update Rhymen/go-whatsapp. Fixes #1107 (#1109) (make whatsapp working again) | ||||
| - discord: Add an ID cache (discord). Fixes #1106 (#1111) (fix delete/edits with webhooks) | ||||
|  | ||||
| # v1.17.3 | ||||
|  | ||||
| ## Enhancements | ||||
|  | ||||
| - xmpp: Implement User Avatar spoofing of XMPP users #1090 | ||||
| - rocketchat: Relay Joins/Topic changes in RocketChat bridge (#1085) | ||||
| - irc: Add JoinDelay option (irc). Fixes #1084 (#1098) | ||||
| - slack: Clip too long messages on 3000 length (slack). Fixes #1081 (#1102) | ||||
|  | ||||
| ## Bugfix | ||||
|  | ||||
| - general: Fix the behavior of ShowTopicChange and SyncTopic (#1086) | ||||
| - slack: Prevent image/message looping (slack). Fixes #1088 (#1096) | ||||
| - whatsapp: Ignore non-critical errors (whatsapp). Fixes #1094 (#1100) | ||||
| - irc: Add extra space before colon in attachments (irc). Fixes #1089 (#1101) | ||||
|  | ||||
| This release couldn't exist without the following contributors: | ||||
| @42wim, @ldruschk, @qaisjp, @Polynomdivision | ||||
|  | ||||
| # v1.17.2 | ||||
|  | ||||
| ## Enhancements | ||||
|  | ||||
| - slack: Update vendor slack-go/slack (#1068) | ||||
| - general: Update vendor d5/tengo (#1066) | ||||
| - general: Clarify terminology used in mapping group chat IDs to channels in config (#1079) | ||||
|  | ||||
| ## Bugfix | ||||
|  | ||||
| - whatsapp: Update Rhymen/go-whatsapp vendor and whatsapp version (#1078). Fixes Media upload #1074 | ||||
| - whatsapp: Reset start timestamp on reconnect (whatsapp). Fixes #1059 (#1064) | ||||
|  | ||||
| This release couldn't exist without the following contributors: | ||||
| @42wim, @jheiselman | ||||
|  | ||||
| # v1.17.1 | ||||
|  | ||||
| ## Enhancements | ||||
|  | ||||
| - docker: Remove build dependencies from final image (multistage build) #1057 | ||||
|  | ||||
| ## Bugfix | ||||
|  | ||||
| - general: Don't transmit typing events from ourselves #1056 | ||||
| - general: Add support for build tags #1054 | ||||
| - discord: Strip extra info from emotes (discord) #1052 | ||||
| - msteams: fix macos build: Update vendor yaegashi/msgraph.go to v0.1.2 #1036 | ||||
| - whatsapp: Update client version whatsapp. Fixes #1061 #1062 | ||||
|  | ||||
| This release couldn't exist without the following contributors: | ||||
| @awigen, @qaisjp, @42wim | ||||
|  | ||||
| # v1.17.0 | ||||
|  | ||||
| ## New features | ||||
|  | ||||
| - msteams: new protocol added. Add initial Microsoft Teams support #967 | ||||
|   See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup for a complete walkthrough | ||||
| - discord: Add ability to procure avatars from the destination bridge #1000 | ||||
| - matrix: Add support for avatars from matrix. #1007 | ||||
| - general: support JSON and YAML config formats #1045 | ||||
|  | ||||
| ## Enhancements | ||||
|  | ||||
| - discord: Check only bridged channels for PermManageWebhooks #1001 | ||||
| - irc: Be less lossy when throttling IRC messages #1004 | ||||
| - keybase: updated library #1002, #1019 | ||||
| - matrix: Rebase gomatrix vendor with upstream #1006 | ||||
| - slack: Use upstream slack-go/slack again #1018 | ||||
| - slack: Ignore ConnectingEvent #1041 | ||||
| - slack: use blocks not attachments #1048 | ||||
| - sshchat: Update vendor shazow/ssh-chat #1029 | ||||
| - telegram: added markdownv2 mode for telegram #1037 | ||||
| - whatsapp: Implement basic reconnect (whatsapp). Fixes #987 #1003 | ||||
|  | ||||
| ## Bugfix | ||||
|  | ||||
| - discord: Fix webhook permission checks sometimes failing #1043 | ||||
| - discord: Fix #1027: warning when handling inbound webhooks #1044 | ||||
| - discord: Fix duplicate separator on empty description/url (discord) #1035 | ||||
| - matrix: Fix issue with underscores in links #999 | ||||
| - slack: Fix #1039: messages sent to Slack being synced back #1046 | ||||
| - telegram: Make avatars download work with mediaserverdownload (telegram). Fixes #920  | ||||
|  | ||||
| This release couldn't exist without the following contributors: | ||||
| @qaisjp, @jakubgs, @burner1024, @notpushkin, @MartijnBraam, @42wim | ||||
|  | ||||
| # v1.16.5 | ||||
|  | ||||
| - Fix version bump | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -u -e -x -o pipefail | ||||
|  | ||||
| go version | grep go1.13 || exit | ||||
| go version | grep go1.14 || exit | ||||
|  | ||||
| VERSION=$(git describe --tags) | ||||
| mkdir ci/binaries | ||||
|   | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !noapi | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/api" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["api"] = api.New | ||||
| } | ||||
							
								
								
									
										12
									
								
								gateway/bridgemap/bdiscord.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								gateway/bridgemap/bdiscord.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| // +build !nodiscord | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bdiscord "github.com/42wim/matterbridge/bridge/discord" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["discord"] = bdiscord.New | ||||
| 	UserTypingSupport["discord"] = struct{}{} | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bgitter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bgitter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nogitter | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bgitter "github.com/42wim/matterbridge/bridge/gitter" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["gitter"] = bgitter.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/birc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/birc.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !noirc | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	birc "github.com/42wim/matterbridge/bridge/irc" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["irc"] = birc.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bkeybase.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bkeybase.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nokeybase | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bkeybase "github.com/42wim/matterbridge/bridge/keybase" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["keybase"] = bkeybase.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bmatrix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmatrix.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nomatrix | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bmatrix "github.com/42wim/matterbridge/bridge/matrix" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["matrix"] = bmatrix.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bmattermost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmattermost.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nomattermost | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bmattermost "github.com/42wim/matterbridge/bridge/mattermost" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["mattermost"] = bmattermost.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bmsteams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmsteams.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nomsteams | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bmsteams "github.com/42wim/matterbridge/bridge/msteams" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["msteams"] = bmsteams.New | ||||
| } | ||||
| @@ -2,45 +2,9 @@ package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/api" | ||||
| 	bdiscord "github.com/42wim/matterbridge/bridge/discord" | ||||
| 	bgitter "github.com/42wim/matterbridge/bridge/gitter" | ||||
| 	birc "github.com/42wim/matterbridge/bridge/irc" | ||||
| 	bkeybase "github.com/42wim/matterbridge/bridge/keybase" | ||||
| 	bmatrix "github.com/42wim/matterbridge/bridge/matrix" | ||||
| 	bmattermost "github.com/42wim/matterbridge/bridge/mattermost" | ||||
| 	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat" | ||||
| 	bslack "github.com/42wim/matterbridge/bridge/slack" | ||||
| 	bsshchat "github.com/42wim/matterbridge/bridge/sshchat" | ||||
| 	bsteam "github.com/42wim/matterbridge/bridge/steam" | ||||
| 	btelegram "github.com/42wim/matterbridge/bridge/telegram" | ||||
| 	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp" | ||||
| 	bxmpp "github.com/42wim/matterbridge/bridge/xmpp" | ||||
| 	bzulip "github.com/42wim/matterbridge/bridge/zulip" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	FullMap = map[string]bridge.Factory{ | ||||
| 		"api":          api.New, | ||||
| 		"discord":      bdiscord.New, | ||||
| 		"gitter":       bgitter.New, | ||||
| 		"irc":          birc.New, | ||||
| 		"mattermost":   bmattermost.New, | ||||
| 		"matrix":       bmatrix.New, | ||||
| 		"rocketchat":   brocketchat.New, | ||||
| 		"slack-legacy": bslack.NewLegacy, | ||||
| 		"slack":        bslack.New, | ||||
| 		"sshchat":      bsshchat.New, | ||||
| 		"steam":        bsteam.New, | ||||
| 		"telegram":     btelegram.New, | ||||
| 		"whatsapp":     bwhatsapp.New, | ||||
| 		"xmpp":         bxmpp.New, | ||||
| 		"zulip":        bzulip.New, | ||||
| 		"keybase":      bkeybase.New, | ||||
| 	} | ||||
|  | ||||
| 	UserTypingSupport = map[string]struct{}{ | ||||
| 		"slack":   {}, | ||||
| 		"discord": {}, | ||||
| 	} | ||||
| 	FullMap           = map[string]bridge.Factory{} | ||||
| 	UserTypingSupport = map[string]struct{}{} | ||||
| ) | ||||
|   | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !norocketchat | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["rocketchat"] = brocketchat.New | ||||
| } | ||||
							
								
								
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // +build !noslack | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bslack "github.com/42wim/matterbridge/bridge/slack" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["slack-legacy"] = bslack.NewLegacy | ||||
| 	FullMap["slack"] = bslack.New | ||||
| 	UserTypingSupport["slack"] = struct{}{} | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nosshchat | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bsshchat "github.com/42wim/matterbridge/bridge/sshchat" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["sshchat"] = bsshchat.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nosteam | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bsteam "github.com/42wim/matterbridge/bridge/steam" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["steam"] = bsteam.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !notelegram | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	btelegram "github.com/42wim/matterbridge/bridge/telegram" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["telegram"] = btelegram.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nowhatsapp | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["whatsapp"] = bwhatsapp.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !noxmpp | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bxmpp "github.com/42wim/matterbridge/bridge/xmpp" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["xmpp"] = bxmpp.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nozulip | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bzulip "github.com/42wim/matterbridge/bridge/zulip" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["zulip"] = bzulip.New | ||||
| } | ||||
| @@ -108,7 +108,7 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | ||||
| func (gw *Gateway) checkConfig(cfg *config.Bridge) { | ||||
| 	match := false | ||||
| 	for _, key := range gw.Router.Config.Viper().AllKeys() { | ||||
| 		if strings.HasPrefix(key, cfg.Account) { | ||||
| 		if strings.HasPrefix(key, strings.ToLower(cfg.Account)) { | ||||
| 			match = true | ||||
| 			break | ||||
| 		} | ||||
| @@ -306,8 +306,6 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool { | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) string { | ||||
| 	br := gw.Bridges[msg.Account] | ||||
| 	msg.Protocol = br.Protocol | ||||
| 	if dest.GetBool("StripNick") { | ||||
| 		re := regexp.MustCompile("[^a-zA-Z0-9]+") | ||||
| 		msg.Username = re.ReplaceAllString(msg.Username, "") | ||||
| @@ -315,6 +313,7 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri | ||||
| 	nick := dest.GetString("RemoteNickFormat") | ||||
|  | ||||
| 	// loop to replace nicks | ||||
| 	br := gw.Bridges[msg.Account] | ||||
| 	for _, outer := range br.GetStringSlice2D("ReplaceNicks") { | ||||
| 		search := outer[0] | ||||
| 		replace := outer[1] | ||||
|   | ||||
| @@ -169,7 +169,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool { | ||||
| 	switch event { | ||||
| 	case config.EventAvatarDownload: | ||||
| 		// Avatar downloads are only relevant for telegram and mattermost for now | ||||
| 		if dest.Protocol != "mattermost" && dest.Protocol != "telegram" { | ||||
| 		if dest.Protocol != "mattermost" && dest.Protocol != "telegram" && dest.Protocol != "xmpp" { | ||||
| 			return true | ||||
| 		} | ||||
| 	case config.EventJoinLeave: | ||||
| @@ -179,7 +179,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool { | ||||
| 		} | ||||
| 	case config.EventTopicChange: | ||||
| 		// only relay topic change when used in some way on other side | ||||
| 		if dest.GetBool("ShowTopicChange") && dest.GetBool("SyncTopic") { | ||||
| 		if !dest.GetBool("ShowTopicChange") && !dest.GetBool("SyncTopic") { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -132,6 +132,9 @@ func (r *Router) handleReceive() { | ||||
| 		r.handleEventFailure(&msg) | ||||
| 		r.handleEventRejoinChannels(&msg) | ||||
|  | ||||
| 		// Set message protocol based on the account it came from | ||||
| 		msg.Protocol = r.getBridge(msg.Account).Protocol | ||||
|  | ||||
| 		filesHandled := false | ||||
| 		for _, gw := range r.Gateways { | ||||
| 			// record all the message ID's of the different bridges | ||||
|   | ||||
							
								
								
									
										49
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								go.mod
									
									
									
									
									
								
							| @@ -5,36 +5,37 @@ require ( | ||||
| 	github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f | ||||
| 	github.com/Jeffail/gabs v1.1.1 // indirect | ||||
| 	github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 | ||||
| 	github.com/Rhymen/go-whatsapp v0.1.0 | ||||
| 	github.com/bwmarrin/discordgo v0.20.2 | ||||
| 	github.com/d5/tengo/v2 v2.0.2 | ||||
| 	github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec | ||||
| 	github.com/fsnotify/fsnotify v1.4.7 | ||||
| 	github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 | ||||
| 	github.com/d5/tengo/v2 v2.4.2 | ||||
| 	github.com/davecgh/go-spew v1.1.1 | ||||
| 	github.com/fsnotify/fsnotify v1.4.9 | ||||
| 	github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible | ||||
| 	github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 | ||||
| 	github.com/google/gops v0.3.6 | ||||
| 	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect | ||||
| 	github.com/gorilla/schema v1.1.0 | ||||
| 	github.com/gorilla/websocket v1.4.1 | ||||
| 	github.com/hashicorp/golang-lru v0.5.3 | ||||
| 	github.com/gorilla/websocket v1.4.2 | ||||
| 	github.com/hashicorp/golang-lru v0.5.4 | ||||
| 	github.com/hpcloud/tail v1.0.0 // indirect | ||||
| 	github.com/jpillora/backoff v1.0.0 | ||||
| 	github.com/keybase/go-keybase-chat-bot v0.0.0-20190816161829-561f10822eb2 | ||||
| 	github.com/labstack/echo/v4 v4.1.13 | ||||
| 	github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da | ||||
| 	github.com/labstack/echo/v4 v4.1.16 | ||||
| 	github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 | ||||
| 	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d | ||||
| 	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 | ||||
| 	github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 | ||||
| 	github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible | ||||
| 	github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 | ||||
| 	github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca | ||||
| 	github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 | ||||
| 	github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 | ||||
| 	github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 | ||||
| 	github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 | ||||
| 	github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba | ||||
| 	github.com/mattermost/mattermost-server v5.5.0+incompatible | ||||
| 	github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561 | ||||
| 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect | ||||
| 	github.com/missdeer/golib v1.0.3 | ||||
| 	github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect | ||||
| 	github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect | ||||
| 	github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 | ||||
| 	github.com/nicksnyder/go-i18n v1.4.0 // indirect | ||||
| 	github.com/nlopes/slack v0.6.0 | ||||
| 	github.com/onsi/ginkgo v1.6.0 // indirect | ||||
| 	github.com/onsi/gomega v1.4.1 // indirect | ||||
| 	github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c | ||||
| @@ -42,21 +43,23 @@ require ( | ||||
| 	github.com/rs/xid v1.2.1 | ||||
| 	github.com/russross/blackfriday v1.5.2 | ||||
| 	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca | ||||
| 	github.com/shazow/ssh-chat v1.8.2 | ||||
| 	github.com/sirupsen/logrus v1.4.2 | ||||
| 	github.com/spf13/viper v1.6.1 | ||||
| 	github.com/stretchr/testify v1.4.0 | ||||
| 	github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98 | ||||
| 	github.com/sirupsen/logrus v1.6.0 | ||||
| 	github.com/slack-go/slack v0.6.4 | ||||
| 	github.com/spf13/viper v1.7.0 | ||||
| 	github.com/stretchr/testify v1.5.1 | ||||
| 	github.com/technoweenie/multipartstreamer v1.0.1 // indirect | ||||
| 	github.com/writeas/go-strip-markdown v2.0.1+incompatible | ||||
| 	github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect | ||||
| 	github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 | ||||
| 	golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 | ||||
| 	github.com/yaegashi/msgraph.go v0.1.2 | ||||
| 	github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 | ||||
| 	golang.org/x/image v0.0.0-20200430140353-33d19683fad8 | ||||
| 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | ||||
| 	gopkg.in/fsnotify.v1 v1.4.7 // indirect | ||||
| 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | ||||
| 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect | ||||
| ) | ||||
|  | ||||
| replace github.com/nlopes/slack v0.6.0 => github.com/matterbridge/slack v0.1.1-0.20191208194820-95190f11bfb6 | ||||
|  | ||||
| replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 | ||||
| //replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 | ||||
|  | ||||
| go 1.13 | ||||
|   | ||||
							
								
								
									
										305
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										305
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,75 +1,106 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= | ||||
| cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= | ||||
| cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= | ||||
| cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= | ||||
| cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= | ||||
| cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= | ||||
| cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= | ||||
| cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= | ||||
| cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= | ||||
| cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg= | ||||
| github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y= | ||||
| github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= | ||||
| github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= | ||||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E= | ||||
| github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ= | ||||
| github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg= | ||||
| github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= | ||||
| github.com/Rhymen/go-whatsapp v0.1.0 h1:XTXhFIQ/fx9jKObUnUX2Q+nh58EyeHNhX7DniE8xeuA= | ||||
| github.com/Rhymen/go-whatsapp v0.1.0/go.mod h1:xJSy+okeRjKkQEH/lEYrnekXB3PG33fqL0I6ncAkV50= | ||||
| github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 h1:kb1zvD+xd+XbPUdQ0lMxnRaQ76N5C9vMAClLi8Dyw1Y= | ||||
| github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk= | ||||
| github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME= | ||||
| github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU= | ||||
| github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw= | ||||
| github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM= | ||||
| github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e h1:IHXQQIpxASe3m0Jtcd3XongL+lxHNd5nUmvHxJARUmg= | ||||
| github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc= | ||||
| github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= | ||||
| github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= | ||||
| github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= | ||||
| github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= | ||||
| github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | ||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||
| github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= | ||||
| github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | ||||
| github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= | ||||
| github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | ||||
| github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= | ||||
| github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||||
| github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= | ||||
| github.com/d5/tengo/v2 v2.0.2 h1:3APkPZPc1FExaJoWrN5YzvDqc6GNkQH6ehmCRDmN83I= | ||||
| github.com/d5/tengo/v2 v2.0.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= | ||||
| github.com/d5/tengo/v2 v2.4.2 h1:Sj+v0CS8jtrZrXyJLzWof6QTnE6S5k3tYP0y6YVPGkw= | ||||
| github.com/d5/tengo/v2 v2.4.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX87a2zVnnbPPickIM9Gf9OIhsIgWQ= | ||||
| github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY= | ||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||||
| github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | ||||
| github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= | ||||
| github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | ||||
| github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||||
| github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= | ||||
| github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | ||||
| github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||
| github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= | ||||
| github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= | ||||
| github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= | ||||
| github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= | ||||
| github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= | ||||
| github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= | ||||
| github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= | ||||
| github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE= | ||||
| github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= | ||||
| github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= | ||||
| github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= | ||||
| github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | ||||
| github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= | ||||
| github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= | ||||
| github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= | ||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 h1:wJkj+x9gPYlDyM34C6r3SXPs270coWeh85wu1CsusDo= | ||||
| github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= | ||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to= | ||||
| github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
| github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||
| github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo= | ||||
| github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= | ||||
| @@ -77,17 +108,37 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR | ||||
| github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= | ||||
| github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= | ||||
| github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= | ||||
| github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= | ||||
| github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | ||||
| github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= | ||||
| github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= | ||||
| github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= | ||||
| github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= | ||||
| github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= | ||||
| github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= | ||||
| github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | ||||
| github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | ||||
| github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= | ||||
| github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= | ||||
| github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= | ||||
| github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= | ||||
| github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= | ||||
| github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= | ||||
| github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | ||||
| github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= | ||||
| github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= | ||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||
| github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||
| github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= | ||||
| github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= | ||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | ||||
| github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | ||||
| github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= | ||||
| github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= | ||||
| github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= | ||||
| github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= | ||||
| github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= | ||||
| github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= | ||||
| github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||||
| @@ -95,72 +146,84 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS | ||||
| github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= | ||||
| github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | ||||
| github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= | ||||
| github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= | ||||
| github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | ||||
| github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | ||||
| github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||
| github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= | ||||
| github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= | ||||
| github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= | ||||
| github.com/keybase/go-keybase-chat-bot v0.0.0-20190816161829-561f10822eb2 h1:zacJswvfPqUSGdcBXJzKvLN/dB1UjDGDvDesMBBzoA4= | ||||
| github.com/keybase/go-keybase-chat-bot v0.0.0-20190816161829-561f10822eb2/go.mod h1:vNc28YFzigVJod0j5EbuTtRIe7swx8vodh2yA4jZ2s8= | ||||
| github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da h1:LK+8uBG3kNikj664cjFt88RBmuGmonxkXv2rUVfbqz4= | ||||
| github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da/go.mod h1:xJA+X9ZVyT/irGldcb7q1XnJBq5F9s5H9h2L44Y+poY= | ||||
| github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999 h1:2d+FLQbz4xRTi36DO1qYNUwfORax9XcQ0jhbO81Vago= | ||||
| github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ= | ||||
| github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= | ||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||
| github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= | ||||
| github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= | ||||
| github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= | ||||
| github.com/labstack/echo/v4 v4.1.13 h1:JYgKq6NQQSaKbQcsOadAKX1kUVLCUzLGwu8sxN5tC34= | ||||
| github.com/labstack/echo/v4 v4.1.13/go.mod h1:3WZNypykZ3tnqpF2Qb4fPg27XDunFqgP3HGDmCMgv7U= | ||||
| github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= | ||||
| github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= | ||||
| github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= | ||||
| github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= | ||||
| github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 h1:BS9tqL0OCiOGuy/CYYk2gc33fxqaqh5/rhqMKu4tcYA= | ||||
| github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I= | ||||
| github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= | ||||
| github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | ||||
| github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs= | ||||
| github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= | ||||
| github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 h1:xTcLiEPMp9jVh/lHEPpLc87RZ4sRWRZe0rM578/waOk= | ||||
| github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= | ||||
| github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 h1:B9HaistmV+MD8/33BXmZe1zPIn+RImAFVXNNSOrwU2E= | ||||
| github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= | ||||
| github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw= | ||||
| github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y= | ||||
| github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI= | ||||
| github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4= | ||||
| github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k= | ||||
| github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q= | ||||
| github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca h1:3ypqEpFpt6vg5Sv2xxA8/v4WiSOnWMXW7DqxTxpM4XI= | ||||
| github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g= | ||||
| github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 h1:kWkP1lXpkvtoNL08jkP3XQH/zvDOEXJpdCJd/DlIvMw= | ||||
| github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q= | ||||
| github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0= | ||||
| github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g= | ||||
| github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho= | ||||
| github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA= | ||||
| github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE= | ||||
| github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU= | ||||
| github.com/matterbridge/slack v0.1.1-0.20191208194820-95190f11bfb6 h1:UvXXR9tHYqJUXZVEtiK2qkEWBXfFneicate5kOshVFk= | ||||
| github.com/matterbridge/slack v0.1.1-0.20191208194820-95190f11bfb6/go.mod h1:2uCJim0Ct2z1Uj+XQq47KCLLC1b/9UTYaZOvDtbZfK4= | ||||
| github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba h1:XleOY4IjAEIcxAh+IFwT5JT5Ze3RHiYz6m+4ZfZ0rc0= | ||||
| github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU= | ||||
| github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU= | ||||
| github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y= | ||||
| github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= | ||||
| github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= | ||||
| github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= | ||||
| github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= | ||||
| github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | ||||
| github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= | ||||
| github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | ||||
| github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= | ||||
| github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= | ||||
| github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= | ||||
| github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | ||||
| github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||
| github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= | ||||
| github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||
| github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= | ||||
| github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= | ||||
| github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= | ||||
| github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | ||||
| github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | ||||
| github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | ||||
| github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= | ||||
| github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||||
| github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561 h1:0YGo77enc6tJpXQxUeQWs9bPIQPTH1lbOmc5tgRuq4o= | ||||
| github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561/go.mod h1:/ivCKurgV/bx6yqtP/Jtc2Xmrv3beCYBvlfAUl4X5g4= | ||||
| github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | ||||
| github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= | ||||
| github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= | ||||
| github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= | ||||
| github.com/missdeer/golib v1.0.3 h1:+kz/tn1lXlPS8i+gjHHVAZC8YcgrmfiMTqELyvOwI4g= | ||||
| github.com/missdeer/golib v1.0.3/go.mod h1:Cys1ITPPZxIk2eTcQcKT3jDsBdhICAfrrw+ki/eRXxA= | ||||
| github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= | ||||
| github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||||
| github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= | ||||
| github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= | ||||
| github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= | ||||
| github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | ||||
| github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= | ||||
| github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | ||||
| github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o= | ||||
| github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= | ||||
| github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9DtP8gnTmNtMxbPyhYltfxsITel8g= | ||||
| @@ -175,6 +238,7 @@ github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= | ||||
| github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | ||||
| github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= | ||||
| github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= | ||||
| github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= | ||||
| github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw= | ||||
| github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4= | ||||
| github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcUhvI17yO9SlpAdDIJGkhDEgO8A= | ||||
| @@ -186,6 +250,7 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | ||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= | ||||
| github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= | ||||
| github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= | ||||
| github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | ||||
| @@ -195,26 +260,33 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 | ||||
| github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | ||||
| github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= | ||||
| github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= | ||||
| github.com/rickb777/date v1.12.4 h1:+6IzcCCS/1t17DrmnEvrznyq7nM8vPwir6/UhlyohKw= | ||||
| github.com/rickb777/date v1.12.4/go.mod h1:xP0eo/I5qmUt97yRGClHZfyLZ3ikMw6v6SU5MOGZTE0= | ||||
| github.com/rickb777/plural v1.2.0 h1:5tvEc7UBCZ7l8h/2UeybSkt/uu1DQsZFOFdNevmUhlE= | ||||
| github.com/rickb777/plural v1.2.0/go.mod h1:UdpyWFCGbo3mvK3f/PfZOAOrkjzJlYN/sD46XNWJ+Es= | ||||
| github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
| github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= | ||||
| github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= | ||||
| github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= | ||||
| github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | ||||
| github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= | ||||
| github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= | ||||
| github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= | ||||
| github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= | ||||
| github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0= | ||||
| github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI= | ||||
| github.com/shazow/ssh-chat v1.8.2 h1:MMso9eWfCnPBelRsusYxKcRBUwHIPEQkR9WrO89II38= | ||||
| github.com/shazow/ssh-chat v1.8.2/go.mod h1:cXTZK/D1zujEwB0y8DIT1GX8rIKjyLDYeWd+jitPX84= | ||||
| github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7 h1:80VN+vGkqM773Br/uNNTSheo3KatTgV8IpjIKjvVLng= | ||||
| github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98 h1:sN07ff+PSRsUNhpSod4uGKAQ+Nc0FXsBPG9FmYMNg4w= | ||||
| github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98/go.mod h1:xkTgfD+WP+KR4HuG76oal25BBEeu5kJyi2EOsgiu/4Q= | ||||
| github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||||
| github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= | ||||
| github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= | ||||
| github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | ||||
| github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= | ||||
| github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | ||||
| github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= | ||||
| github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= | ||||
| github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE= | ||||
| github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= | ||||
| github.com/slack-go/slack v0.6.4 h1:cxOqFgM5RW6mdEyDqAJutFk3qiORK9oHRKi5bPqkY9o= | ||||
| github.com/slack-go/slack v0.6.4/go.mod h1:sGRjv3w+ERAUMMMbldHObQPBcNSyVB7KLKYfnwUFBfw= | ||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | ||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||
| github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= | ||||
| @@ -229,35 +301,39 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9 | ||||
| github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= | ||||
| github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= | ||||
| github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | ||||
| github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= | ||||
| github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= | ||||
| github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= | ||||
| github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= | ||||
| github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= | ||||
| github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= | ||||
| github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= | ||||
| github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= | ||||
| github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= | ||||
| github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= | ||||
| github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||||
| github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||||
| github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= | ||||
| github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= | ||||
| github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= | ||||
| github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= | ||||
| github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw= | ||||
| github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE= | ||||
| github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= | ||||
| github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= | ||||
| github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= | ||||
| github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6 h1:YdYsPAZ2pC6Tow/nPZOPQ96O3hm/ToAkGsPLzedXERk= | ||||
| github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= | ||||
| github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= | ||||
| github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo= | ||||
| github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= | ||||
| github.com/yaegashi/msgraph.go v0.1.2 h1:83uVRQaj8YBsVqOUGj0WRwzxdgGF69jRpg5IQYaTvoY= | ||||
| github.com/yaegashi/msgraph.go v0.1.2/go.mod h1:Lp39e9oo596G5FcmMKI0cXR3mg/QikSdabgZdbMqbAM= | ||||
| github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 h1:UQwvu7FjUEdVYofx0U6bsc5odNE7wa5TSA0fl559GcA= | ||||
| github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= | ||||
| go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | ||||
| go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= | ||||
| go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | ||||
| go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= | ||||
| go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||||
| go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= | ||||
| @@ -266,66 +342,141 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= | ||||
| go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= | ||||
| golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= | ||||
| golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
| golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
| golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
| golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= | ||||
| golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0= | ||||
| golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= | ||||
| golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
| golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= | ||||
| golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= | ||||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | ||||
| golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= | ||||
| golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | ||||
| golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | ||||
| golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= | ||||
| golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= | ||||
| golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= | ||||
| golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= | ||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= | ||||
| golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20171017063910-8dbc5d05d6ed/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= | ||||
| golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | ||||
| golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
| golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | ||||
| google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | ||||
| google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= | ||||
| google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= | ||||
| google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||
| google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | ||||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||
| gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | ||||
| gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||
| gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= | ||||
| @@ -337,10 +488,14 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep | ||||
| gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | ||||
| gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | ||||
| gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= | ||||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | ||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| rsc.io/goversion v1.0.0 h1:/IhXBiai89TyuerPquiZZ39IQkTfAUbZB2awsyYZ/2c= | ||||
| honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | ||||
| rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | ||||
| rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| @@ -15,7 +16,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	version = "1.16.5" | ||||
| 	version = "1.17.5" | ||||
| 	githash string | ||||
|  | ||||
| 	flagConfig  = flag.String("conf", "matterbridge.toml", "config file") | ||||
| @@ -67,17 +68,31 @@ func setupLogger() *logrus.Logger { | ||||
| 		Formatter: &prefixed.TextFormatter{ | ||||
| 			PrefixPadding: 13, | ||||
| 			DisableColors: true, | ||||
| 			FullTimestamp: true, | ||||
| 		}, | ||||
| 		Level: logrus.InfoLevel, | ||||
| 	} | ||||
| 	if *flagDebug || os.Getenv("DEBUG") == "1" { | ||||
| 		logger.SetReportCaller(true) | ||||
| 		logger.Formatter = &prefixed.TextFormatter{ | ||||
| 			PrefixPadding:   13, | ||||
| 			DisableColors:   true, | ||||
| 			FullTimestamp:   false, | ||||
| 			ForceFormatting: true, | ||||
| 			PrefixPadding: 13, | ||||
| 			DisableColors: true, | ||||
| 			FullTimestamp: false, | ||||
|  | ||||
| 			CallerFormatter: func(function, file string) string { | ||||
| 				return fmt.Sprintf(" [%s:%s]", function, file) | ||||
| 			}, | ||||
| 			CallerPrettyfier: func(f *runtime.Frame) (string, string) { | ||||
| 				sp := strings.SplitAfter(f.File, "/matterbridge/") | ||||
| 				filename := f.File | ||||
| 				if len(sp) > 1 { | ||||
| 					filename = sp[1] | ||||
| 				} | ||||
| 				s := strings.Split(f.Function, ".") | ||||
| 				funcName := s[len(s)-1] | ||||
| 				return funcName, fmt.Sprintf("%s:%d", filename, f.Line) | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		logger.Level = logrus.DebugLevel | ||||
| 		logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.") | ||||
| 	} | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -14,7 +14,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gorilla/schema" | ||||
| 	"github.com/nlopes/slack" | ||||
| 	"github.com/slack-go/slack" | ||||
| ) | ||||
|  | ||||
| // OMessage for mattermost incoming webhook. (send to mattermost) | ||||
|   | ||||
							
								
								
									
										1481
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1481
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										78
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -56,6 +56,8 @@ message Location { | ||||
| } | ||||
|  | ||||
| message Point { | ||||
|     optional int32 xDeprecated = 1; | ||||
|     optional int32 yDeprecated = 2; | ||||
|     optional double x = 3; | ||||
|     optional double y = 4; | ||||
| } | ||||
| @@ -93,6 +95,7 @@ message ContextInfo { | ||||
|     optional AdReplyInfo quotedAd = 23; | ||||
|     optional MessageKey placeholderKey = 24; | ||||
|     optional uint32 expiration = 25; | ||||
|     optional int64 ephemeralSettingTimestamp = 26; | ||||
| } | ||||
|  | ||||
| message SenderKeyDistributionMessage { | ||||
| @@ -136,6 +139,11 @@ message LocationMessage { | ||||
|     optional string name = 3; | ||||
|     optional string address = 4; | ||||
|     optional string url = 5; | ||||
|     optional bool isLive = 6; | ||||
|     optional uint32 accuracyInMeters = 7; | ||||
|     optional float speedInMps = 8; | ||||
|     optional uint32 degreesClockwiseFromMagneticNorth = 9; | ||||
|     optional string comment = 11; | ||||
|     optional bytes jpegThumbnail = 16; | ||||
|     optional ContextInfo contextInfo = 17; | ||||
| } | ||||
| @@ -238,9 +246,29 @@ message ProtocolMessage { | ||||
|     enum PROTOCOL_MESSAGE_TYPE { | ||||
|         REVOKE = 0; | ||||
|         EPHEMERAL_SETTING = 3; | ||||
|         EPHEMERAL_SYNC_RESPONSE = 4; | ||||
|         HISTORY_SYNC_NOTIFICATION = 5; | ||||
|     } | ||||
|     optional PROTOCOL_MESSAGE_TYPE type = 2; | ||||
|     optional uint32 ephemeralExpiration = 4; | ||||
|     optional int64 ephemeralSettingTimestamp = 5; | ||||
|     optional HistorySyncNotification historySyncNotification = 6; | ||||
| } | ||||
|  | ||||
| message HistorySyncNotification { | ||||
|     optional bytes fileSha256 = 1; | ||||
|     optional uint64 fileLength = 2; | ||||
|     optional bytes mediaKey = 3; | ||||
|     optional bytes fileEncSha256 = 4; | ||||
|     optional string directPath = 5; | ||||
|     enum HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE { | ||||
|         INITIAL_BOOTSTRAP = 0; | ||||
|         INITIAL_STATUS_V3 = 1; | ||||
|         FULL = 2; | ||||
|         RECENT = 3; | ||||
|     } | ||||
|     optional HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE syncType = 6; | ||||
|     optional uint32 chunkOrder = 7; | ||||
| } | ||||
|  | ||||
| message ContactsArrayMessage { | ||||
| @@ -355,6 +383,8 @@ message StickerMessage { | ||||
|     optional int64 mediaKeyTimestamp = 10; | ||||
|     optional uint32 firstFrameLength = 11; | ||||
|     optional bytes firstFrameSidecar = 12; | ||||
|     optional bool isAnimated = 13; | ||||
|     optional bytes pngThumbnail = 16; | ||||
|     optional ContextInfo contextInfo = 17; | ||||
| } | ||||
|  | ||||
| @@ -401,6 +431,12 @@ message TemplateButtonReplyMessage { | ||||
|     optional uint32 selectedIndex = 4; | ||||
| } | ||||
|  | ||||
| message CatalogSnapshot { | ||||
|     optional ImageMessage catalogImage = 1; | ||||
|     optional string title = 2; | ||||
|     optional string description = 3; | ||||
| } | ||||
|  | ||||
| message ProductSnapshot { | ||||
|     optional ImageMessage productImage = 1; | ||||
|     optional string productId = 2; | ||||
| @@ -417,6 +453,7 @@ message ProductSnapshot { | ||||
| message ProductMessage { | ||||
|     optional ProductSnapshot product = 1; | ||||
|     optional string businessOwnerJid = 2; | ||||
|     optional CatalogSnapshot catalog = 4; | ||||
|     optional ContextInfo contextInfo = 17; | ||||
| } | ||||
|  | ||||
| @@ -513,6 +550,8 @@ message WebFeatures { | ||||
|     optional WEB_FEATURES_FLAG templateMessage = 30; | ||||
|     optional WEB_FEATURES_FLAG templateMessageInteractivity = 31; | ||||
|     optional WEB_FEATURES_FLAG ephemeralMessages = 32; | ||||
|     optional WEB_FEATURES_FLAG e2ENotificationSync = 33; | ||||
|     optional WEB_FEATURES_FLAG recentStickersV2 = 34; | ||||
| } | ||||
|  | ||||
| message TabletNotificationsInfo { | ||||
| @@ -537,6 +576,11 @@ message WebNotificationsInfo { | ||||
| } | ||||
|  | ||||
| message PaymentInfo { | ||||
|     enum PAYMENT_INFO_CURRENCY { | ||||
|         UNKNOWN_CURRENCY = 0; | ||||
|         INR = 1; | ||||
|     } | ||||
|     optional PAYMENT_INFO_CURRENCY currencyDeprecated = 1; | ||||
|     optional uint64 amount1000 = 2; | ||||
|     optional string receiverJid = 3; | ||||
|     enum PAYMENT_INFO_STATUS { | ||||
| @@ -559,6 +603,37 @@ message PaymentInfo { | ||||
|     optional uint64 expiryTimestamp = 7; | ||||
|     optional bool futureproofed = 8; | ||||
|     optional string currency = 9; | ||||
|     enum PAYMENT_INFO_TXNSTATUS { | ||||
|         UNKNOWN = 0; | ||||
|         PENDING_SETUP = 1; | ||||
|         PENDING_RECEIVER_SETUP = 2; | ||||
|         INIT = 3; | ||||
|         SUCCESS = 4; | ||||
|         COMPLETED = 5; | ||||
|         FAILED = 6; | ||||
|         FAILED_RISK = 7; | ||||
|         FAILED_PROCESSING = 8; | ||||
|         FAILED_RECEIVER_PROCESSING = 9; | ||||
|         FAILED_DA = 10; | ||||
|         FAILED_DA_FINAL = 11; | ||||
|         REFUNDED_TXN = 12; | ||||
|         REFUND_FAILED = 13; | ||||
|         REFUND_FAILED_PROCESSING = 14; | ||||
|         REFUND_FAILED_DA = 15; | ||||
|         EXPIRED_TXN = 16; | ||||
|         AUTH_CANCELED = 17; | ||||
|         AUTH_CANCEL_FAILED_PROCESSING = 18; | ||||
|         AUTH_CANCEL_FAILED = 19; | ||||
|         COLLECT_INIT = 20; | ||||
|         COLLECT_SUCCESS = 21; | ||||
|         COLLECT_FAILED = 22; | ||||
|         COLLECT_FAILED_RISK = 23; | ||||
|         COLLECT_REJECTED = 24; | ||||
|         COLLECT_EXPIRED = 25; | ||||
|         COLLECT_CANCELED = 26; | ||||
|         COLLECT_CANCELLING = 27; | ||||
|     } | ||||
|     optional PAYMENT_INFO_TXNSTATUS txnStatus = 10; | ||||
| } | ||||
|  | ||||
| message WebMessageInfo { | ||||
| @@ -668,4 +743,5 @@ message WebMessageInfo { | ||||
|     optional PaymentInfo quotedPaymentInfo = 31; | ||||
|     optional uint64 ephemeralStartTimestamp = 32; | ||||
|     optional uint32 ephemeralDuration = 33; | ||||
| } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -90,6 +90,7 @@ type Conn struct { | ||||
|  | ||||
| 	longClientName  string | ||||
| 	shortClientName string | ||||
| 	clientVersion   string | ||||
|  | ||||
| 	loginSessionLock sync.RWMutex | ||||
| 	Proxy            func(*http.Request) (*url.URL, error) | ||||
| @@ -121,6 +122,7 @@ func NewConn(timeout time.Duration) (*Conn, error) { | ||||
|  | ||||
| 		longClientName:  "github.com/rhymen/go-whatsapp", | ||||
| 		shortClientName: "go-whatsapp", | ||||
| 		clientVersion:   "0.1.0", | ||||
| 	} | ||||
| 	return wac, wac.connect() | ||||
| } | ||||
| @@ -135,6 +137,7 @@ func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL | ||||
|  | ||||
| 		longClientName:  "github.com/rhymen/go-whatsapp", | ||||
| 		shortClientName: "go-whatsapp", | ||||
| 		clientVersion:   "0.1.0", | ||||
| 		Proxy:           proxy, | ||||
| 	} | ||||
| 	return wac, wac.connect() | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ require ( | ||||
| 	github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect | ||||
| 	github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect | ||||
| 	github.com/golang/protobuf v1.3.0 | ||||
| 	github.com/gorilla/websocket v1.4.0 | ||||
| 	github.com/gorilla/websocket v1.4.1 | ||||
| 	github.com/pkg/errors v0.8.1 | ||||
| 	golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,8 +12,9 @@ github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= | ||||
| github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= | ||||
| github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= | ||||
| github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= | ||||
| github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= | ||||
| github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= | ||||
|   | ||||
							
								
								
									
										116
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,10 +10,8 @@ import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"mime/multipart" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"net/url" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/Rhymen/go-whatsapp/crypto/cbc" | ||||
| @@ -95,7 +93,50 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) { | ||||
| 	return data[:n-10], data[n-10 : n], nil | ||||
| } | ||||
|  | ||||
| func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) { | ||||
| type MediaConn struct { | ||||
| 	Status    int `json:"status"` | ||||
| 	MediaConn struct { | ||||
| 		Auth  string `json:"auth"` | ||||
| 		TTL   int    `json:"ttl"` | ||||
| 		Hosts []struct { | ||||
| 			Hostname string   `json:"hostname"` | ||||
| 			IPs      []string `json:"ips"` | ||||
| 		} `json:"hosts"` | ||||
| 	} `json:"media_conn"` | ||||
| } | ||||
|  | ||||
| func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) { | ||||
| 	queryReq := []interface{}{"query", "mediaConn"} | ||||
| 	ch, err := wac.writeJson(queryReq) | ||||
| 	if err != nil { | ||||
| 		return "", "", 0, err | ||||
| 	} | ||||
|  | ||||
| 	var resp MediaConn | ||||
| 	select { | ||||
| 	case r := <-ch: | ||||
| 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||
| 			return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err) | ||||
| 		} | ||||
| 	case <-time.After(wac.msgTimeout): | ||||
| 		return "", "", 0, fmt.Errorf("query media conn timed out") | ||||
| 	} | ||||
|  | ||||
| 	if resp.Status != 200 { | ||||
| 		return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status) | ||||
| 	} | ||||
|  | ||||
| 	return resp.MediaConn.Hosts[0].Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil | ||||
| } | ||||
|  | ||||
| var mediaTypeMap = map[MediaType]string{ | ||||
| 	MediaImage: "/mms/image", | ||||
| 	MediaVideo: "/mms/video", | ||||
| 	MediaDocument: "/mms/document", | ||||
| 	MediaAudio: "/mms/audio", | ||||
| } | ||||
|  | ||||
| func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) { | ||||
| 	data, err := ioutil.ReadAll(reader) | ||||
| 	if err != nil { | ||||
| 		return "", nil, nil, nil, 0, err | ||||
| @@ -128,67 +169,30 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK | ||||
| 	sha.Write(append(enc, mac...)) | ||||
| 	fileEncSha256 = sha.Sum(nil) | ||||
|  | ||||
| 	var filetype string | ||||
| 	switch appInfo { | ||||
| 	case MediaImage: | ||||
| 		filetype = "image" | ||||
| 	case MediaAudio: | ||||
| 		filetype = "audio" | ||||
| 	case MediaDocument: | ||||
| 		filetype = "document" | ||||
| 	case MediaVideo: | ||||
| 		filetype = "video" | ||||
| 	hostname, auth, _, err := wac.queryMediaConn() | ||||
| 	token := base64.URLEncoding.EncodeToString(fileEncSha256) | ||||
| 	q := url.Values{ | ||||
| 		"auth":  []string{auth}, | ||||
| 		"token": []string{token}, | ||||
| 	} | ||||
| 	path := mediaTypeMap[appInfo] | ||||
| 	uploadURL := url.URL{ | ||||
| 		Scheme:   "https", | ||||
| 		Host:     hostname, | ||||
| 		Path:     fmt.Sprintf("%s/%s", path, token), | ||||
| 		RawQuery: q.Encode(), | ||||
| 	} | ||||
|  | ||||
| 	uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)} | ||||
| 	ch, err := wac.writeJson(uploadReq) | ||||
| 	body := bytes.NewReader(append(enc, mac...)) | ||||
|  | ||||
| 	req, err := http.NewRequest("POST", uploadURL.String(), body) | ||||
| 	if err != nil { | ||||
| 		return "", nil, nil, nil, 0, err | ||||
| 	} | ||||
|  | ||||
| 	var resp map[string]interface{} | ||||
| 	select { | ||||
| 	case r := <-ch: | ||||
| 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||
| 			return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v", err) | ||||
| 		} | ||||
| 	case <-time.After(wac.msgTimeout): | ||||
| 		return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out") | ||||
| 	} | ||||
|  | ||||
| 	if int(resp["status"].(float64)) != 200 { | ||||
| 		return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"]) | ||||
| 	} | ||||
|  | ||||
| 	var b bytes.Buffer | ||||
| 	w := multipart.NewWriter(&b) | ||||
| 	hashWriter, err := w.CreateFormField("hash") | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "%v\n", err) | ||||
| 	} | ||||
| 	io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256))) | ||||
|  | ||||
| 	fileWriter, err := w.CreateFormFile("file", "blob") | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "%v\n", err) | ||||
| 	} | ||||
| 	io.Copy(fileWriter, bytes.NewReader(append(enc, mac...))) | ||||
| 	err = w.Close() | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "%v\n", err) | ||||
| 	} | ||||
|  | ||||
| 	req, err := http.NewRequest("POST", resp["url"].(string), &b) | ||||
| 	if err != nil { | ||||
| 		return "", nil, nil, nil, 0, err | ||||
| 	} | ||||
|  | ||||
| 	req.Header.Set("Content-Type", w.FormDataContentType()) | ||||
| 	req.Header.Set("Origin", "https://web.whatsapp.com") | ||||
| 	req.Header.Set("Referer", "https://web.whatsapp.com/") | ||||
|  | ||||
| 	req.URL.Query().Set("f", "j") | ||||
|  | ||||
| 	client := &http.Client{} | ||||
| 	// Submit the request | ||||
| 	res, err := client.Do(req) | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -15,7 +15,10 @@ import ( | ||||
| ) | ||||
|  | ||||
| func (wac *Conn) readPump() { | ||||
| 	defer wac.wg.Done() | ||||
| 	defer func() { | ||||
| 		wac.wg.Done() | ||||
| 		_, _ = wac.Disconnect() | ||||
| 	}() | ||||
|  | ||||
| 	var readErr error | ||||
| 	var msgType int | ||||
| @@ -31,7 +34,6 @@ func (wac *Conn) readPump() { | ||||
| 		case <-readerFound: | ||||
| 			if readErr != nil { | ||||
| 				wac.handle(&ErrConnectionFailed{Err: readErr}) | ||||
| 				_, _ = wac.Disconnect() | ||||
| 				return | ||||
| 			} | ||||
| 			msg, err := ioutil.ReadAll(reader) | ||||
|   | ||||
							
								
								
									
										18
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -18,7 +18,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| //represents the WhatsAppWeb client version | ||||
| var waVersion = []int{0, 3, 3324} | ||||
| var waVersion = []int{0, 4, 2080} | ||||
|  | ||||
| /* | ||||
| Session contains session individual information. To be able to resume the connection without scanning the qr code | ||||
| @@ -107,10 +107,10 @@ func CheckCurrentServerVersion() ([]int, error) { | ||||
| 	} | ||||
|  | ||||
| 	b64ClientId := base64.StdEncoding.EncodeToString(clientId) | ||||
| 	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true} | ||||
| 	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true} | ||||
| 	loginChan, err := wac.writeJson(login) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error writing login", err) | ||||
| 		return nil, fmt.Errorf("error writing login: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	// Retrieve an answer from the websocket | ||||
| @@ -123,7 +123,7 @@ func CheckCurrentServerVersion() ([]int, error) { | ||||
|  | ||||
| 	var resp map[string]interface{} | ||||
| 	if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||
| 		return nil, fmt.Errorf("error decoding login", err) | ||||
| 		return nil, fmt.Errorf("error decoding login: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	// Take the curr property as X.Y.Z and split it into as int slice | ||||
| @@ -141,17 +141,17 @@ func CheckCurrentServerVersion() ([]int, error) { | ||||
| SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the | ||||
| WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible. | ||||
| */ | ||||
| func (wac *Conn) SetClientName(long, short string) error { | ||||
| func (wac *Conn) SetClientName(long, short, version string) error { | ||||
| 	if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) { | ||||
| 		return fmt.Errorf("cannot change client name after logging in") | ||||
| 	} | ||||
| 	wac.longClientName, wac.shortClientName = long, short | ||||
| 	wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| /* | ||||
| SetClientVersion sets WhatsApp client version | ||||
| Default value is 0.3.3324 | ||||
| Default value is 0.4.2080 | ||||
| */ | ||||
| func (wac *Conn) SetClientVersion(major int, minor int, patch int) { | ||||
| 	waVersion = []int{major, minor, patch} | ||||
| @@ -213,7 +213,7 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | ||||
| 	} | ||||
|  | ||||
| 	session.ClientId = base64.StdEncoding.EncodeToString(clientId) | ||||
| 	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true} | ||||
| 	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true} | ||||
| 	loginChan, err := wac.writeJson(login) | ||||
| 	if err != nil { | ||||
| 		return session, fmt.Errorf("error writing login: %v\n", err) | ||||
| @@ -369,7 +369,7 @@ func (wac *Conn) Restore() error { | ||||
| 	wac.listener.Unlock() | ||||
|  | ||||
| 	//admin init | ||||
| 	init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true} | ||||
| 	init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true} | ||||
| 	initChan, err := wac.writeJson(init) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error writing admin init: %v\n", err) | ||||
|   | ||||
							
								
								
									
										7
									
								
								vendor/github.com/d5/tengo/v2/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/d5/tengo/v2/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -11,9 +11,10 @@ builds: | ||||
|       - darwin | ||||
|       - linux | ||||
|       - windows | ||||
| archive: | ||||
|   files: | ||||
|     - none* | ||||
| archives: | ||||
|   - | ||||
|     files: | ||||
|       - none* | ||||
| checksum: | ||||
|   name_template: 'checksums.txt' | ||||
| changelog: | ||||
|   | ||||
							
								
								
									
										1
									
								
								vendor/github.com/d5/tengo/v2/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/d5/tengo/v2/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,6 +6,7 @@ lint: | ||||
|  | ||||
| test: generate lint | ||||
| 	go test -race -cover ./... | ||||
| 	go run ./cmd/tengo -resolve ./testdata/cli/test.tengo | ||||
|  | ||||
| fmt: | ||||
| 	go fmt ./... | ||||
|   | ||||
							
								
								
									
										40
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,9 +5,8 @@ | ||||
| # The Tengo Language | ||||
|  | ||||
| [](https://godoc.org/github.com/d5/tengo) | ||||
|  | ||||
| [](https://goreportcard.com/report/github.com/d5/tengo) | ||||
| [](https://circleci.com/gh/d5/tengo) | ||||
| [](https://sourcegraph.com/github.com/d5/tengo?badge) | ||||
|  | ||||
| **Tengo is a small, dynamic, fast, secure script language for Go.**  | ||||
|  | ||||
| @@ -52,19 +51,21 @@ fmt.println(sum("", [1, 2, 3]))  // "123" | ||||
|  | ||||
| ## Benchmark | ||||
|  | ||||
| | | fib(35) | fibt(35) |  Type  | | ||||
| | | fib(35) | fibt(35) |  Language (Type)  | | ||||
| | :--- |    ---: |     ---: |  :---: | | ||||
| | Go | `48ms` | `3ms` | Go (native) | | ||||
| | [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go | | ||||
| | Lua | `1,416ms` | `3ms` | Lua (native) | | ||||
| | [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go | | ||||
| | [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go | | ||||
| | Python | `2,588ms` | `26ms` | Python (native) | | ||||
| | [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go | | ||||
| | [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go | | ||||
| | [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go | | ||||
| | [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go | | ||||
| | [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go | | ||||
| | [**Tengo**](https://github.com/d5/tengo) | `2,931ms` | `4ms` | Tengo (VM) | | ||||
| | [go-lua](https://github.com/Shopify/go-lua) | `4,824ms` | `4ms` | Lua (VM) | | ||||
| | [GopherLua](https://github.com/yuin/gopher-lua) | `5,365ms` | `4ms` | Lua (VM) | | ||||
| | [goja](https://github.com/dop251/goja) | `5,533ms` | `5ms` | JavaScript (VM) | | ||||
| | [starlark-go](https://github.com/google/starlark-go) | `11,495ms` | `5ms` | Starlark (Interpreter) | | ||||
| | [Yaegi](https://github.com/containous/yaegi) | `15,645ms` | `12ms` | Yaegi (Interpreter) | | ||||
| | [gpython](https://github.com/go-python/gpython) | `16,322ms` | `5ms` | Python (Interpreter) | | ||||
| | [otto](https://github.com/robertkrimen/otto) | `73,093ms` | `10ms` | JavaScript (Interpreter) | | ||||
| | [Anko](https://github.com/mattn/anko) | `79,809ms` | `8ms` | Anko (Interpreter) | | ||||
| | - | - | - | - | | ||||
| | Go | `53ms` | `3ms` | Go (Native) | | ||||
| | Lua | `1,612ms` | `3ms` | Lua (Native) | | ||||
| | Python | `2,632ms` | `23ms` | Python 2 (Native) | | ||||
|  | ||||
| _* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): | ||||
| Fibonacci(35)_   | ||||
| @@ -75,6 +76,10 @@ _* See [here](https://github.com/d5/tengobench) for commands/codes used_ | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
| ``` | ||||
| go get github.com/d5/tengo/v2 | ||||
| ``` | ||||
|  | ||||
| A simple Go example code that compiles/runs Tengo script code with some input/output values: | ||||
|  | ||||
| ```golang | ||||
| @@ -133,3 +138,10 @@ each([a, b, c, d], func(x) { | ||||
| - [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md) | ||||
| - [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) | ||||
| - [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md) | ||||
| - Syntax Highlighters: [VSCode](https://github.com/lissein/vscode-tengo), [Atom](https://github.com/d5/tengo-atom) | ||||
| - **Why the name Tengo?** It's from [1Q84](https://en.wikipedia.org/wiki/1Q84). | ||||
|  | ||||
| ## | ||||
|  | ||||
| :hearts: Like writing Go code? Come work at Skool. [We're hiring!](https://jobs.lever.co/skool) | ||||
|  | ||||
|   | ||||
							
								
								
									
										109
									
								
								vendor/github.com/d5/tengo/v2/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/d5/tengo/v2/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,6 +13,14 @@ var builtinFuncs = []*BuiltinFunction{ | ||||
| 		Name:  "append", | ||||
| 		Value: builtinAppend, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Name:  "delete", | ||||
| 		Value: builtinDelete, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Name:  "splice", | ||||
| 		Value: builtinSplice, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Name:  "string", | ||||
| 		Value: builtinString, | ||||
| @@ -500,3 +508,104 @@ func builtinAppend(args ...Object) (Object, error) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // builtinDelete deletes Map keys | ||||
| // usage: delete(map, "key") | ||||
| // key must be a string | ||||
| func builtinDelete(args ...Object) (Object, error) { | ||||
| 	argsLen := len(args) | ||||
| 	if argsLen != 2 { | ||||
| 		return nil, ErrWrongNumArguments | ||||
| 	} | ||||
| 	switch arg := args[0].(type) { | ||||
| 	case *Map: | ||||
| 		if key, ok := args[1].(*String); ok { | ||||
| 			delete(arg.Value, key.Value) | ||||
| 			return UndefinedValue, nil | ||||
| 		} | ||||
| 		return nil, ErrInvalidArgumentType{ | ||||
| 			Name:     "second", | ||||
| 			Expected: "string", | ||||
| 			Found:    args[1].TypeName(), | ||||
| 		} | ||||
| 	default: | ||||
| 		return nil, ErrInvalidArgumentType{ | ||||
| 			Name:     "first", | ||||
| 			Expected: "map", | ||||
| 			Found:    arg.TypeName(), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // builtinSplice deletes and changes given Array, returns deleted items. | ||||
| // usage: | ||||
| // deleted_items := splice(array[,start[,delete_count[,item1[,item2[,...]]]]) | ||||
| func builtinSplice(args ...Object) (Object, error) { | ||||
| 	argsLen := len(args) | ||||
| 	if argsLen == 0 { | ||||
| 		return nil, ErrWrongNumArguments | ||||
| 	} | ||||
|  | ||||
| 	array, ok := args[0].(*Array) | ||||
| 	if !ok { | ||||
| 		return nil, ErrInvalidArgumentType{ | ||||
| 			Name:     "first", | ||||
| 			Expected: "array", | ||||
| 			Found:    args[0].TypeName(), | ||||
| 		} | ||||
| 	} | ||||
| 	arrayLen := len(array.Value) | ||||
|  | ||||
| 	var startIdx int | ||||
| 	if argsLen > 1 { | ||||
| 		arg1, ok := args[1].(*Int) | ||||
| 		if !ok { | ||||
| 			return nil, ErrInvalidArgumentType{ | ||||
| 				Name:     "second", | ||||
| 				Expected: "int", | ||||
| 				Found:    args[1].TypeName(), | ||||
| 			} | ||||
| 		} | ||||
| 		startIdx = int(arg1.Value) | ||||
| 		if startIdx < 0 || startIdx > arrayLen { | ||||
| 			return nil, ErrIndexOutOfBounds | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	delCount := len(array.Value) | ||||
| 	if argsLen > 2 { | ||||
| 		arg2, ok := args[2].(*Int) | ||||
| 		if !ok { | ||||
| 			return nil, ErrInvalidArgumentType{ | ||||
| 				Name:     "third", | ||||
| 				Expected: "int", | ||||
| 				Found:    args[2].TypeName(), | ||||
| 			} | ||||
| 		} | ||||
| 		delCount = int(arg2.Value) | ||||
| 		if delCount < 0 { | ||||
| 			return nil, ErrIndexOutOfBounds | ||||
| 		} | ||||
| 	} | ||||
| 	// if count of to be deleted items is bigger than expected, truncate it | ||||
| 	if startIdx+delCount > arrayLen { | ||||
| 		delCount = arrayLen - startIdx | ||||
| 	} | ||||
| 	// delete items | ||||
| 	endIdx := startIdx + delCount | ||||
| 	deleted := append([]Object{}, array.Value[startIdx:endIdx]...) | ||||
|  | ||||
| 	head := array.Value[:startIdx] | ||||
| 	var items []Object | ||||
| 	if argsLen > 3 { | ||||
| 		items = make([]Object, 0, argsLen-3) | ||||
| 		for i := 3; i < argsLen; i++ { | ||||
| 			items = append(items, args[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	items = append(items, array.Value[endIdx:]...) | ||||
| 	array.Value = append(head, items...) | ||||
|  | ||||
| 	// return deleted items | ||||
| 	return &Array{Value: deleted}, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										12
									
								
								vendor/github.com/d5/tengo/v2/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/d5/tengo/v2/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -97,6 +97,7 @@ func (b *Bytecode) RemoveDuplicates() { | ||||
| 	var deduped []Object | ||||
|  | ||||
| 	indexMap := make(map[int]int) // mapping from old constant index to new index | ||||
| 	fns := make(map[*CompiledFunction]int) | ||||
| 	ints := make(map[int64]int) | ||||
| 	strings := make(map[string]int) | ||||
| 	floats := make(map[float64]int) | ||||
| @@ -106,9 +107,14 @@ func (b *Bytecode) RemoveDuplicates() { | ||||
| 	for curIdx, c := range b.Constants { | ||||
| 		switch c := c.(type) { | ||||
| 		case *CompiledFunction: | ||||
| 			// add to deduped list | ||||
| 			indexMap[curIdx] = len(deduped) | ||||
| 			deduped = append(deduped, c) | ||||
| 			if newIdx, ok := fns[c]; ok { | ||||
| 				indexMap[curIdx] = newIdx | ||||
| 			} else { | ||||
| 				newIdx = len(deduped) | ||||
| 				fns[c] = newIdx | ||||
| 				indexMap[curIdx] = newIdx | ||||
| 				deduped = append(deduped, c) | ||||
| 			} | ||||
| 		case *ImmutableMap: | ||||
| 			modName := inferModuleName(c) | ||||
| 			newIdx, ok := immutableMaps[modName] | ||||
|   | ||||
							
								
								
									
										42
									
								
								vendor/github.com/d5/tengo/v2/compiler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/d5/tengo/v2/compiler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -44,6 +44,7 @@ type Compiler struct { | ||||
| 	file            *parser.SourceFile | ||||
| 	parent          *Compiler | ||||
| 	modulePath      string | ||||
| 	importDir       string | ||||
| 	constants       []Object | ||||
| 	symbolTable     *SymbolTable | ||||
| 	scopes          []compilationScope | ||||
| @@ -520,7 +521,7 @@ func (c *Compiler) Compile(node parser.Node) error { | ||||
| 			switch v := v.(type) { | ||||
| 			case []byte: // module written in Tengo | ||||
| 				compiled, err := c.compileModule(node, | ||||
| 					node.ModuleName, node.ModuleName, v) | ||||
| 					node.ModuleName, v, false) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -537,24 +538,20 @@ func (c *Compiler) Compile(node parser.Node) error { | ||||
| 				moduleName += ".tengo" | ||||
| 			} | ||||
|  | ||||
| 			modulePath, err := filepath.Abs(moduleName) | ||||
| 			modulePath, err := filepath.Abs( | ||||
| 				filepath.Join(c.importDir, moduleName)) | ||||
| 			if err != nil { | ||||
| 				return c.errorf(node, "module file path error: %s", | ||||
| 					err.Error()) | ||||
| 			} | ||||
|  | ||||
| 			if err := c.checkCyclicImports(node, modulePath); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			moduleSrc, err := ioutil.ReadFile(moduleName) | ||||
| 			moduleSrc, err := ioutil.ReadFile(modulePath) | ||||
| 			if err != nil { | ||||
| 				return c.errorf(node, "module file read error: %s", | ||||
| 					err.Error()) | ||||
| 			} | ||||
|  | ||||
| 			compiled, err := c.compileModule(node, | ||||
| 				moduleName, modulePath, moduleSrc) | ||||
| 			compiled, err := c.compileModule(node, modulePath, moduleSrc, true) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| @@ -634,6 +631,11 @@ func (c *Compiler) EnableFileImport(enable bool) { | ||||
| 	c.allowFileImport = enable | ||||
| } | ||||
|  | ||||
| // SetImportDir sets the initial import directory path for file imports. | ||||
| func (c *Compiler) SetImportDir(dir string) { | ||||
| 	c.importDir = dir | ||||
| } | ||||
|  | ||||
| func (c *Compiler) compileAssign( | ||||
| 	node parser.Node, | ||||
| 	lhs, rhs []parser.Expr, | ||||
| @@ -847,8 +849,8 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error { | ||||
| 	//     ... body ... | ||||
| 	//   } | ||||
| 	// | ||||
| 	// ":it" is a local variable but will be conflict with other user variables | ||||
| 	// because character ":" is not allowed. | ||||
| 	// ":it" is a local variable but it will not conflict with other user variables | ||||
| 	// because character ":" is not allowed in the variable names. | ||||
|  | ||||
| 	// init | ||||
| 	//   :it = iterator(iterable) | ||||
| @@ -893,6 +895,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error { | ||||
| 		if keySymbol.Scope == ScopeGlobal { | ||||
| 			c.emit(stmt, parser.OpSetGlobal, keySymbol.Index) | ||||
| 		} else { | ||||
| 			keySymbol.LocalAssigned = true | ||||
| 			c.emit(stmt, parser.OpDefineLocal, keySymbol.Index) | ||||
| 		} | ||||
| 	} | ||||
| @@ -909,6 +912,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error { | ||||
| 		if valueSymbol.Scope == ScopeGlobal { | ||||
| 			c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index) | ||||
| 		} else { | ||||
| 			valueSymbol.LocalAssigned = true | ||||
| 			c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index) | ||||
| 		} | ||||
| 	} | ||||
| @@ -955,8 +959,9 @@ func (c *Compiler) checkCyclicImports( | ||||
|  | ||||
| func (c *Compiler) compileModule( | ||||
| 	node parser.Node, | ||||
| 	moduleName, modulePath string, | ||||
| 	modulePath string, | ||||
| 	src []byte, | ||||
| 	isFile bool, | ||||
| ) (*CompiledFunction, error) { | ||||
| 	if err := c.checkCyclicImports(node, modulePath); err != nil { | ||||
| 		return nil, err | ||||
| @@ -967,7 +972,7 @@ func (c *Compiler) compileModule( | ||||
| 		return compiledModule, nil | ||||
| 	} | ||||
|  | ||||
| 	modFile := c.file.Set().AddFile(moduleName, -1, len(src)) | ||||
| 	modFile := c.file.Set().AddFile(modulePath, -1, len(src)) | ||||
| 	p := parser.NewParser(modFile, src, nil) | ||||
| 	file, err := p.ParseFile() | ||||
| 	if err != nil { | ||||
| @@ -984,7 +989,7 @@ func (c *Compiler) compileModule( | ||||
| 	symbolTable = symbolTable.Fork(false) | ||||
|  | ||||
| 	// compile module | ||||
| 	moduleCompiler := c.fork(modFile, modulePath, symbolTable) | ||||
| 	moduleCompiler := c.fork(modFile, modulePath, symbolTable, isFile) | ||||
| 	if err := moduleCompiler.Compile(file); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -1082,10 +1087,16 @@ func (c *Compiler) fork( | ||||
| 	file *parser.SourceFile, | ||||
| 	modulePath string, | ||||
| 	symbolTable *SymbolTable, | ||||
| 	isFile bool, | ||||
| ) *Compiler { | ||||
| 	child := NewCompiler(file, symbolTable, nil, c.modules, c.trace) | ||||
| 	child.modulePath = modulePath // module file path | ||||
| 	child.parent = c              // parent to set to current compiler | ||||
| 	child.allowFileImport = c.allowFileImport | ||||
| 	child.importDir = c.importDir | ||||
| 	if isFile && c.importDir != "" { | ||||
| 		child.importDir = filepath.Dir(modulePath) | ||||
| 	} | ||||
| 	return child | ||||
| } | ||||
|  | ||||
| @@ -1192,6 +1203,7 @@ func (c *Compiler) optimizeFunc(node parser.Node) { | ||||
| 	var lastOp parser.Opcode | ||||
| 	var appendReturn bool | ||||
| 	endPos := len(c.scopes[c.scopeIndex].Instructions) | ||||
| 	newEndPost := len(newInsts) | ||||
| 	iterateInstructions(newInsts, | ||||
| 		func(pos int, opcode parser.Opcode, operands []int) bool { | ||||
| 			switch opcode { | ||||
| @@ -1204,6 +1216,8 @@ func (c *Compiler) optimizeFunc(node parser.Node) { | ||||
| 				} else if endPos == operands[0] { | ||||
| 					// there's a jump instruction that jumps to the end of | ||||
| 					// function compiler should append "return". | ||||
| 					copy(newInsts[pos:], | ||||
| 						MakeInstruction(opcode, newEndPost)) | ||||
| 					appendReturn = true | ||||
| 				} else { | ||||
| 					panic(fmt.Errorf("invalid jump position: %d", newDst)) | ||||
|   | ||||
							
								
								
									
										13
									
								
								vendor/github.com/d5/tengo/v2/script.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/d5/tengo/v2/script.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ package tengo | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"path/filepath" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/d5/tengo/v2/parser" | ||||
| @@ -16,6 +17,7 @@ type Script struct { | ||||
| 	maxAllocs        int64 | ||||
| 	maxConstObjects  int | ||||
| 	enableFileImport bool | ||||
| 	importDir        string | ||||
| } | ||||
|  | ||||
| // NewScript creates a Script instance with an input script. | ||||
| @@ -56,6 +58,16 @@ func (s *Script) SetImports(modules *ModuleMap) { | ||||
| 	s.modules = modules | ||||
| } | ||||
|  | ||||
| // SetImportDir sets the initial import directory for script files. | ||||
| func (s *Script) SetImportDir(dir string) error { | ||||
| 	dir, err := filepath.Abs(dir) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.importDir = dir | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SetMaxAllocs sets the maximum number of objects allocations during the run | ||||
| // time. Compiled script will return ErrObjectAllocLimit error if it | ||||
| // exceeds this limit. | ||||
| @@ -93,6 +105,7 @@ func (s *Script) Compile() (*Compiled, error) { | ||||
|  | ||||
| 	c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil) | ||||
| 	c.EnableFileImport(s.enableFileImport) | ||||
| 	c.SetImportDir(s.importDir) | ||||
| 	if err := c.Compile(file); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										195
									
								
								vendor/github.com/d5/tengo/v2/stdlib/json/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										195
									
								
								vendor/github.com/d5/tengo/v2/stdlib/json/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,20 +1,129 @@ | ||||
| // A modified version of Go's JSON implementation. | ||||
|  | ||||
| // Copyright 2010 The Go Authors. All rights reserved. | ||||
| // Copyright 2010, 2020 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package json | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/d5/tengo/v2" | ||||
| ) | ||||
|  | ||||
| // safeSet holds the value true if the ASCII character with the given array | ||||
| // position can be represented inside a JSON string without any further | ||||
| // escaping. | ||||
| // | ||||
| // All values are true except for the ASCII control characters (0-31), the | ||||
| // double quote ("), and the backslash character ("\"). | ||||
| var safeSet = [utf8.RuneSelf]bool{ | ||||
| 	' ':      true, | ||||
| 	'!':      true, | ||||
| 	'"':      false, | ||||
| 	'#':      true, | ||||
| 	'$':      true, | ||||
| 	'%':      true, | ||||
| 	'&':      true, | ||||
| 	'\'':     true, | ||||
| 	'(':      true, | ||||
| 	')':      true, | ||||
| 	'*':      true, | ||||
| 	'+':      true, | ||||
| 	',':      true, | ||||
| 	'-':      true, | ||||
| 	'.':      true, | ||||
| 	'/':      true, | ||||
| 	'0':      true, | ||||
| 	'1':      true, | ||||
| 	'2':      true, | ||||
| 	'3':      true, | ||||
| 	'4':      true, | ||||
| 	'5':      true, | ||||
| 	'6':      true, | ||||
| 	'7':      true, | ||||
| 	'8':      true, | ||||
| 	'9':      true, | ||||
| 	':':      true, | ||||
| 	';':      true, | ||||
| 	'<':      true, | ||||
| 	'=':      true, | ||||
| 	'>':      true, | ||||
| 	'?':      true, | ||||
| 	'@':      true, | ||||
| 	'A':      true, | ||||
| 	'B':      true, | ||||
| 	'C':      true, | ||||
| 	'D':      true, | ||||
| 	'E':      true, | ||||
| 	'F':      true, | ||||
| 	'G':      true, | ||||
| 	'H':      true, | ||||
| 	'I':      true, | ||||
| 	'J':      true, | ||||
| 	'K':      true, | ||||
| 	'L':      true, | ||||
| 	'M':      true, | ||||
| 	'N':      true, | ||||
| 	'O':      true, | ||||
| 	'P':      true, | ||||
| 	'Q':      true, | ||||
| 	'R':      true, | ||||
| 	'S':      true, | ||||
| 	'T':      true, | ||||
| 	'U':      true, | ||||
| 	'V':      true, | ||||
| 	'W':      true, | ||||
| 	'X':      true, | ||||
| 	'Y':      true, | ||||
| 	'Z':      true, | ||||
| 	'[':      true, | ||||
| 	'\\':     false, | ||||
| 	']':      true, | ||||
| 	'^':      true, | ||||
| 	'_':      true, | ||||
| 	'`':      true, | ||||
| 	'a':      true, | ||||
| 	'b':      true, | ||||
| 	'c':      true, | ||||
| 	'd':      true, | ||||
| 	'e':      true, | ||||
| 	'f':      true, | ||||
| 	'g':      true, | ||||
| 	'h':      true, | ||||
| 	'i':      true, | ||||
| 	'j':      true, | ||||
| 	'k':      true, | ||||
| 	'l':      true, | ||||
| 	'm':      true, | ||||
| 	'n':      true, | ||||
| 	'o':      true, | ||||
| 	'p':      true, | ||||
| 	'q':      true, | ||||
| 	'r':      true, | ||||
| 	's':      true, | ||||
| 	't':      true, | ||||
| 	'u':      true, | ||||
| 	'v':      true, | ||||
| 	'w':      true, | ||||
| 	'x':      true, | ||||
| 	'y':      true, | ||||
| 	'z':      true, | ||||
| 	'{':      true, | ||||
| 	'|':      true, | ||||
| 	'}':      true, | ||||
| 	'~':      true, | ||||
| 	'\u007f': true, | ||||
| } | ||||
|  | ||||
| var hex = "0123456789abcdef" | ||||
|  | ||||
| // Encode returns the JSON encoding of the object. | ||||
| func Encode(o tengo.Object) ([]byte, error) { | ||||
| 	var b []byte | ||||
| @@ -53,7 +162,7 @@ func Encode(o tengo.Object) ([]byte, error) { | ||||
| 		len1 := len(o.Value) - 1 | ||||
| 		idx := 0 | ||||
| 		for key, value := range o.Value { | ||||
| 			b = strconv.AppendQuote(b, key) | ||||
| 			b = encodeString(b, key) | ||||
| 			b = append(b, ':') | ||||
| 			eb, err := Encode(value) | ||||
| 			if err != nil { | ||||
| @@ -71,7 +180,7 @@ func Encode(o tengo.Object) ([]byte, error) { | ||||
| 		len1 := len(o.Value) - 1 | ||||
| 		idx := 0 | ||||
| 		for key, value := range o.Value { | ||||
| 			b = strconv.AppendQuote(b, key) | ||||
| 			b = encodeString(b, key) | ||||
| 			b = append(b, ':') | ||||
| 			eb, err := Encode(value) | ||||
| 			if err != nil { | ||||
| @@ -130,7 +239,9 @@ func Encode(o tengo.Object) ([]byte, error) { | ||||
| 	case *tengo.Int: | ||||
| 		b = strconv.AppendInt(b, o.Value, 10) | ||||
| 	case *tengo.String: | ||||
| 		b = strconv.AppendQuote(b, o.Value) | ||||
| 		// string encoding bug is fixed with newly introduced function | ||||
| 		// encodeString(). See: https://github.com/d5/tengo/issues/268 | ||||
| 		b = encodeString(b, o.Value) | ||||
| 	case *tengo.Time: | ||||
| 		y, err := o.Value.MarshalJSON() | ||||
| 		if err != nil { | ||||
| @@ -144,3 +255,79 @@ func Encode(o tengo.Object) ([]byte, error) { | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // encodeString encodes given string as JSON string according to | ||||
| // https://www.json.org/img/string.png | ||||
| // Implementation is inspired by https://github.com/json-iterator/go | ||||
| // See encodeStringSlowPath() for more information. | ||||
| func encodeString(b []byte, val string) []byte { | ||||
| 	valLen := len(val) | ||||
| 	buf := bytes.NewBuffer(b) | ||||
| 	buf.WriteByte('"') | ||||
|  | ||||
| 	// write string, the fast path, without utf8 and escape support | ||||
| 	i := 0 | ||||
| 	for ; i < valLen; i++ { | ||||
| 		c := val[i] | ||||
| 		if c > 31 && c != '"' && c != '\\' { | ||||
| 			buf.WriteByte(c) | ||||
| 		} else { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if i == valLen { | ||||
| 		buf.WriteByte('"') | ||||
| 		return buf.Bytes() | ||||
| 	} | ||||
| 	encodeStringSlowPath(buf, i, val, valLen) | ||||
| 	buf.WriteByte('"') | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| // encodeStringSlowPath is ported from Go 1.14.2 encoding/json package. | ||||
| // U+2028 U+2029 JSONP security holes can be fixed with addition call to | ||||
| // json.html_escape() thus it is removed from the implementation below. | ||||
| // Note: Invalid runes are not checked as they are checked in original | ||||
| // implementation. | ||||
| func encodeStringSlowPath(buf *bytes.Buffer, i int, val string, valLen int) { | ||||
| 	start := i | ||||
| 	for i < valLen { | ||||
| 		if b := val[i]; b < utf8.RuneSelf { | ||||
| 			if safeSet[b] { | ||||
| 				i++ | ||||
| 				continue | ||||
| 			} | ||||
| 			if start < i { | ||||
| 				buf.WriteString(val[start:i]) | ||||
| 			} | ||||
| 			buf.WriteByte('\\') | ||||
| 			switch b { | ||||
| 			case '\\', '"': | ||||
| 				buf.WriteByte(b) | ||||
| 			case '\n': | ||||
| 				buf.WriteByte('n') | ||||
| 			case '\r': | ||||
| 				buf.WriteByte('r') | ||||
| 			case '\t': | ||||
| 				buf.WriteByte('t') | ||||
| 			default: | ||||
| 				// This encodes bytes < 0x20 except for \t, \n and \r. | ||||
| 				// If escapeHTML is set, it also escapes <, >, and & | ||||
| 				// because they can lead to security holes when | ||||
| 				// user-controlled strings are rendered into JSON | ||||
| 				// and served to some browsers. | ||||
| 				buf.WriteString(`u00`) | ||||
| 				buf.WriteByte(hex[b>>4]) | ||||
| 				buf.WriteByte(hex[b&0xF]) | ||||
| 			} | ||||
| 			i++ | ||||
| 			start = i | ||||
| 			continue | ||||
| 		} | ||||
| 		i++ | ||||
| 		continue | ||||
| 	} | ||||
| 	if start < valLen { | ||||
| 		buf.WriteString(val[start:]) | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/d5/tengo/v2/vm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/d5/tengo/v2/vm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -80,14 +80,14 @@ func (v *VM) Run() (err error) { | ||||
| 	if err != nil { | ||||
| 		filePos := v.fileSet.Position( | ||||
| 			v.curFrame.fn.SourcePos(v.ip - 1)) | ||||
| 		err = fmt.Errorf("Runtime Error: %s\n\tat %s", | ||||
| 			err.Error(), filePos) | ||||
| 		err = fmt.Errorf("Runtime Error: %w\n\tat %s", | ||||
| 			err, filePos) | ||||
| 		for v.framesIndex > 1 { | ||||
| 			v.framesIndex-- | ||||
| 			v.curFrame = &v.frames[v.framesIndex-1] | ||||
| 			filePos = v.fileSet.Position( | ||||
| 				v.curFrame.fn.SourcePos(v.curFrame.ip - 1)) | ||||
| 			err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos) | ||||
| 			err = fmt.Errorf("%w\n\tat %s", err, filePos) | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										9
									
								
								vendor/github.com/fsnotify/fsnotify/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/fsnotify/fsnotify/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,5 +1,12 @@ | ||||
| root = true | ||||
|  | ||||
| [*] | ||||
| [*.go] | ||||
| indent_style = tab | ||||
| indent_size = 4 | ||||
| insert_final_newline = true | ||||
|  | ||||
| [*.{yml,yaml}] | ||||
| indent_style = space | ||||
| indent_size = 2 | ||||
| insert_final_newline = true | ||||
| trim_trailing_whitespace = true | ||||
|   | ||||
							
								
								
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| go.sum linguist-generated | ||||
							
								
								
									
										20
									
								
								vendor/github.com/fsnotify/fsnotify/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/fsnotify/fsnotify/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,29 +2,35 @@ sudo: false | ||||
| language: go | ||||
|  | ||||
| go: | ||||
|   - 1.8.x | ||||
|   - 1.9.x | ||||
|   - tip | ||||
|   - "stable" | ||||
|   - "1.11.x" | ||||
|   - "1.10.x" | ||||
|   - "1.9.x" | ||||
|  | ||||
| matrix: | ||||
|   include: | ||||
|     - go: "stable" | ||||
|       env: GOLINT=true | ||||
|   allow_failures: | ||||
|     - go: tip | ||||
|   fast_finish: true | ||||
|  | ||||
| before_script: | ||||
|   - go get -u github.com/golang/lint/golint | ||||
|  | ||||
| before_install: | ||||
|   - if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi | ||||
|  | ||||
| script: | ||||
|   - go test -v --race ./... | ||||
|   - go test --race ./... | ||||
|  | ||||
| after_script: | ||||
|   - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" | ||||
|   - test -z "$(golint ./...     | tee /dev/stderr)" | ||||
|   - if [ ! -z  "${GOLINT}" ]; then echo running golint; golint --set_exit_status  ./...; else echo skipping golint; fi | ||||
|   - go vet ./... | ||||
|  | ||||
| os: | ||||
|   - linux | ||||
|   - osx | ||||
|   - windows | ||||
|  | ||||
| notifications: | ||||
|   email: false | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| Copyright (c) 2012 The Go Authors. All rights reserved. | ||||
| Copyright (c) 2012 fsnotify Authors. All rights reserved. | ||||
| Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
|   | ||||
							
								
								
									
										71
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,16 +10,16 @@ go get -u golang.org/x/sys/... | ||||
|  | ||||
| Cross platform: Windows, Linux, BSD and macOS. | ||||
|  | ||||
| |Adapter   |OS        |Status    | | ||||
| |----------|----------|----------| | ||||
| |inotify   |Linux 2.6.27 or later, Android\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)| | ||||
| |kqueue    |BSD, macOS, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)| | ||||
| |ReadDirectoryChangesW|Windows|Supported [](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| | ||||
| |FSEvents  |macOS         |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| | ||||
| |FEN       |Solaris 11    |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| | ||||
| |fanotify  |Linux 2.6.37+ | | | ||||
| |USN Journals |Windows    |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| | ||||
| |Polling   |*All*         |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| | ||||
| | Adapter               | OS                               | Status                                                                                                                          | | ||||
| | --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | ||||
| | inotify               | Linux 2.6.27 or later, Android\* | Supported [](https://travis-ci.org/fsnotify/fsnotify) | | ||||
| | kqueue                | BSD, macOS, iOS\*                | Supported [](https://travis-ci.org/fsnotify/fsnotify) | | ||||
| | ReadDirectoryChangesW | Windows                          | Supported [](https://travis-ci.org/fsnotify/fsnotify) | | ||||
| | FSEvents              | macOS                            | [Planned](https://github.com/fsnotify/fsnotify/issues/11)                                                                       | | ||||
| | FEN                   | Solaris 11                       | [In Progress](https://github.com/fsnotify/fsnotify/issues/12)                                                                   | | ||||
| | fanotify              | Linux 2.6.37+                    | [Planned](https://github.com/fsnotify/fsnotify/issues/114)                                                                      | | ||||
| | USN Journals          | Windows                          | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)                                                                         | | ||||
| | Polling               | *All*                            | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)                                                                          | | ||||
|  | ||||
| \* Android and iOS are untested. | ||||
|  | ||||
| @@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o | ||||
|  | ||||
| Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| ```go | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/fsnotify/fsnotify" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	watcher, err := fsnotify.NewWatcher() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	defer watcher.Close() | ||||
|  | ||||
| 	done := make(chan bool) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case event, ok := <-watcher.Events: | ||||
| 				if !ok { | ||||
| 					return | ||||
| 				} | ||||
| 				log.Println("event:", event) | ||||
| 				if event.Op&fsnotify.Write == fsnotify.Write { | ||||
| 					log.Println("modified file:", event.Name) | ||||
| 				} | ||||
| 			case err, ok := <-watcher.Errors: | ||||
| 				if !ok { | ||||
| 					return | ||||
| 				} | ||||
| 				log.Println("error:", err) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	err = watcher.Add("/tmp/foo") | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	<-done | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Contributing | ||||
|  | ||||
| Please refer to [CONTRIBUTING][] before opening an issue or pull request. | ||||
| @@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created: | ||||
| * Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. | ||||
| * BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. | ||||
|  | ||||
| **Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** | ||||
|  | ||||
| fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. | ||||
|  | ||||
| [#62]: https://github.com/howeyc/fsnotify/issues/62 | ||||
| [#18]: https://github.com/fsnotify/fsnotify/issues/18 | ||||
| [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -63,4 +63,6 @@ func (e Event) String() string { | ||||
| } | ||||
|  | ||||
| // Common errors that can be reported by a watcher | ||||
| var ErrEventOverflow = errors.New("fsnotify queue overflow") | ||||
| var ( | ||||
| 	ErrEventOverflow = errors.New("fsnotify queue overflow") | ||||
| ) | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/fsnotify/fsnotify/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/fsnotify/fsnotify/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| module github.com/fsnotify/fsnotify | ||||
|  | ||||
| go 1.13 | ||||
|  | ||||
| require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 | ||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= | ||||
| golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
							
								
								
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) { | ||||
| 	poller.fd = fd | ||||
|  | ||||
| 	// Create epoll fd | ||||
| 	poller.epfd, errno = unix.EpollCreate1(0) | ||||
| 	poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) | ||||
| 	if poller.epfd == -1 { | ||||
| 		return nil, errno | ||||
| 	} | ||||
| 	// Create pipe; pipe[0] is the read end, pipe[1] the write end. | ||||
| 	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) | ||||
| 	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,4 +8,4 @@ package fsnotify | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| const openMode = unix.O_NONBLOCK | unix.O_RDONLY | ||||
| const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -9,4 +9,4 @@ package fsnotify | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| // note: this constant is not defined on BSD | ||||
| const openMode = unix.O_EVTONLY | ||||
| const openMode = unix.O_EVTONLY | unix.O_CLOEXEC | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/golang/protobuf/proto/properties.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/golang/protobuf/proto/properties.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -38,7 +38,6 @@ package proto | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| @@ -194,7 +193,7 @@ func (p *Properties) Parse(s string) { | ||||
| 	// "bytes,49,opt,name=foo,def=hello!" | ||||
| 	fields := strings.Split(s, ",") // breaks def=, but handled below. | ||||
| 	if len(fields) < 2 { | ||||
| 		fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s) | ||||
| 		log.Printf("proto: tag has too few fields: %q", s) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @@ -214,7 +213,7 @@ func (p *Properties) Parse(s string) { | ||||
| 		p.WireType = WireBytes | ||||
| 		// no numeric converter for non-numeric types | ||||
| 	default: | ||||
| 		fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s) | ||||
| 		log.Printf("proto: tag has unknown wire type: %q", s) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/gorilla/websocket/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/gorilla/websocket/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,7 +8,7 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the | ||||
|  | ||||
| ### Documentation | ||||
|  | ||||
| * [API Reference](http://godoc.org/github.com/gorilla/websocket) | ||||
| * [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) | ||||
| * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) | ||||
| * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) | ||||
| * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) | ||||
|   | ||||
							
								
								
									
										14
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -244,8 +244,8 @@ type Conn struct { | ||||
| 	subprotocol string | ||||
|  | ||||
| 	// Write fields | ||||
| 	mu            chan bool // used as mutex to protect write to conn | ||||
| 	writeBuf      []byte    // frame is constructed in this buffer. | ||||
| 	mu            chan struct{} // used as mutex to protect write to conn | ||||
| 	writeBuf      []byte        // frame is constructed in this buffer. | ||||
| 	writePool     BufferPool | ||||
| 	writeBufSize  int | ||||
| 	writeDeadline time.Time | ||||
| @@ -302,8 +302,8 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, | ||||
| 		writeBuf = make([]byte, writeBufferSize) | ||||
| 	} | ||||
|  | ||||
| 	mu := make(chan bool, 1) | ||||
| 	mu <- true | ||||
| 	mu := make(chan struct{}, 1) | ||||
| 	mu <- struct{}{} | ||||
| 	c := &Conn{ | ||||
| 		isServer:               isServer, | ||||
| 		br:                     br, | ||||
| @@ -377,7 +377,7 @@ func (c *Conn) read(n int) ([]byte, error) { | ||||
|  | ||||
| func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { | ||||
| 	<-c.mu | ||||
| 	defer func() { c.mu <- true }() | ||||
| 	defer func() { c.mu <- struct{}{} }() | ||||
|  | ||||
| 	c.writeErrMu.Lock() | ||||
| 	err := c.writeErr | ||||
| @@ -429,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | ||||
| 		maskBytes(key, 0, buf[6:]) | ||||
| 	} | ||||
|  | ||||
| 	d := time.Hour * 1000 | ||||
| 	d := 1000 * time.Hour | ||||
| 	if !deadline.IsZero() { | ||||
| 		d = deadline.Sub(time.Now()) | ||||
| 		if d < 0 { | ||||
| @@ -444,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | ||||
| 	case <-timer.C: | ||||
| 		return errWriteTimeout | ||||
| 	} | ||||
| 	defer func() { c.mu <- true }() | ||||
| 	defer func() { c.mu <- struct{}{} }() | ||||
|  | ||||
| 	c.writeErrMu.Lock() | ||||
| 	err := c.writeErr | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -187,9 +187,9 @@ | ||||
| // than the largest message do not provide any benefit. | ||||
| // | ||||
| // Depending on the distribution of message sizes, setting the buffer size to | ||||
| // to a value less than the maximum expected message size can greatly reduce | ||||
| // memory use with a small impact on performance. Here's an example: If 99% of | ||||
| // the messages are smaller than 256 bytes and the maximum message size is 512 | ||||
| // a value less than the maximum expected message size can greatly reduce memory | ||||
| // use with a small impact on performance. Here's an example: If 99% of the | ||||
| // messages are smaller than 256 bytes and the maximum message size is 512 | ||||
| // bytes, then a buffer size of 256 bytes will result in 1.01 more system calls | ||||
| // than a buffer size of 512 bytes. The memory savings is 50%. | ||||
| // | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/gorilla/websocket/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/gorilla/websocket/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | ||||
| github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= | ||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/gorilla/websocket/prepared.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/gorilla/websocket/prepared.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -73,8 +73,8 @@ func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { | ||||
| 		// Prepare a frame using a 'fake' connection. | ||||
| 		// TODO: Refactor code in conn.go to allow more direct construction of | ||||
| 		// the frame. | ||||
| 		mu := make(chan bool, 1) | ||||
| 		mu <- true | ||||
| 		mu := make(chan struct{}, 1) | ||||
| 		mu <- struct{}{} | ||||
| 		var nc prepareConn | ||||
| 		c := &Conn{ | ||||
| 			conn:                   &nc, | ||||
|   | ||||
							
								
								
									
										22
									
								
								vendor/github.com/hashicorp/golang-lru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/hashicorp/golang-lru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -37,7 +37,7 @@ func (c *Cache) Purge() { | ||||
| 	c.lock.Unlock() | ||||
| } | ||||
|  | ||||
| // Add adds a value to the cache.  Returns true if an eviction occurred. | ||||
| // Add adds a value to the cache. Returns true if an eviction occurred. | ||||
| func (c *Cache) Add(key, value interface{}) (evicted bool) { | ||||
| 	c.lock.Lock() | ||||
| 	evicted = c.lru.Add(key, value) | ||||
| @@ -71,8 +71,8 @@ func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) { | ||||
| 	return value, ok | ||||
| } | ||||
|  | ||||
| // ContainsOrAdd checks if a key is in the cache  without updating the | ||||
| // recent-ness or deleting it for being stale,  and if not, adds the value. | ||||
| // ContainsOrAdd checks if a key is in the cache without updating the | ||||
| // recent-ness or deleting it for being stale, and if not, adds the value. | ||||
| // Returns whether found and whether an eviction occurred. | ||||
| func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) { | ||||
| 	c.lock.Lock() | ||||
| @@ -85,6 +85,22 @@ func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) { | ||||
| 	return false, evicted | ||||
| } | ||||
|  | ||||
| // PeekOrAdd checks if a key is in the cache without updating the | ||||
| // recent-ness or deleting it for being stale, and if not, adds the value. | ||||
| // Returns whether found and whether an eviction occurred. | ||||
| func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) { | ||||
| 	c.lock.Lock() | ||||
| 	defer c.lock.Unlock() | ||||
|  | ||||
| 	previous, ok = c.lru.Peek(key) | ||||
| 	if ok { | ||||
| 		return previous, true, false | ||||
| 	} | ||||
|  | ||||
| 	evicted = c.lru.Add(key, value) | ||||
| 	return nil, false, evicted | ||||
| } | ||||
|  | ||||
| // Remove removes the provided key from the cache. | ||||
| func (c *Cache) Remove(key interface{}) (present bool) { | ||||
| 	c.lock.Lock() | ||||
|   | ||||
							
								
								
									
										692
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										692
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,692 @@ | ||||
| package kbchat | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/keybase1" | ||||
| ) | ||||
|  | ||||
| type Thread struct { | ||||
| 	Result chat1.Thread `json:"result"` | ||||
| 	Error  *Error       `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type Inbox struct { | ||||
| 	Result Result `json:"result"` | ||||
| 	Error  *Error `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type sendMessageBody struct { | ||||
| 	Body string | ||||
| } | ||||
|  | ||||
| type sendMessageOptions struct { | ||||
| 	Channel          chat1.ChatChannel `json:"channel,omitempty"` | ||||
| 	ConversationID   chat1.ConvIDStr   `json:"conversation_id,omitempty"` | ||||
| 	Message          sendMessageBody   `json:",omitempty"` | ||||
| 	Filename         string            `json:"filename,omitempty"` | ||||
| 	Title            string            `json:"title,omitempty"` | ||||
| 	MsgID            chat1.MessageID   `json:"message_id,omitempty"` | ||||
| 	ConfirmLumenSend bool              `json:"confirm_lumen_send"` | ||||
| 	ReplyTo          *chat1.MessageID  `json:"reply_to,omitempty"` | ||||
| } | ||||
|  | ||||
| type sendMessageParams struct { | ||||
| 	Options sendMessageOptions | ||||
| } | ||||
|  | ||||
| type sendMessageArg struct { | ||||
| 	Method string | ||||
| 	Params sendMessageParams | ||||
| } | ||||
|  | ||||
| func newSendArg(options sendMessageOptions) sendMessageArg { | ||||
| 	return sendMessageArg{ | ||||
| 		Method: "send", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: options, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetConversations reads all conversations from the current user's inbox. | ||||
| func (a *API) GetConversations(unreadOnly bool) ([]chat1.ConvSummary, error) { | ||||
| 	apiInput := fmt.Sprintf(`{"method":"list", "params": { "options": { "unread_only": %v}}}`, unreadOnly) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var inbox Inbox | ||||
| 	if err := json.Unmarshal(output, &inbox); err != nil { | ||||
| 		return nil, err | ||||
| 	} else if inbox.Error != nil { | ||||
| 		return nil, errors.New(inbox.Error.Message) | ||||
| 	} | ||||
| 	return inbox.Result.Convs, nil | ||||
| } | ||||
|  | ||||
| func (a *API) GetConversation(convID chat1.ConvIDStr) (res chat1.ConvSummary, err error) { | ||||
| 	convIDEscaped, err := json.Marshal(convID) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method":"list", "params": { "options": { "conversation_id": %s}}}`, convIDEscaped) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
|  | ||||
| 	var inbox Inbox | ||||
| 	if err := json.Unmarshal(output, &inbox); err != nil { | ||||
| 		return res, err | ||||
| 	} else if inbox.Error != nil { | ||||
| 		return res, errors.New(inbox.Error.Message) | ||||
| 	} else if len(inbox.Result.Convs) == 0 { | ||||
| 		return res, errors.New("conversation not found") | ||||
| 	} | ||||
| 	return inbox.Result.Convs[0], nil | ||||
| } | ||||
|  | ||||
| // GetTextMessages fetches all text messages from a given channel. Optionally can filter | ||||
| // ont unread status. | ||||
| func (a *API) GetTextMessages(channel chat1.ChatChannel, unreadOnly bool) ([]chat1.MsgSummary, error) { | ||||
| 	channelBytes, err := json.Marshal(channel) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "read", "params": {"options": {"channel": %s}}}`, channelBytes) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var thread Thread | ||||
|  | ||||
| 	if err := json.Unmarshal(output, &thread); err != nil { | ||||
| 		return nil, fmt.Errorf("unable to decode thread: %v", err) | ||||
| 	} else if thread.Error != nil { | ||||
| 		return nil, errors.New(thread.Error.Message) | ||||
| 	} | ||||
|  | ||||
| 	var res []chat1.MsgSummary | ||||
| 	for _, msg := range thread.Result.Messages { | ||||
| 		if msg.Msg.Content.TypeName == "text" { | ||||
| 			res = append(res, *msg.Msg) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return res, nil | ||||
| } | ||||
|  | ||||
| func (a *API) SendMessage(channel chat1.ChatChannel, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: channel, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) Broadcast(body string, args ...interface{}) (SendResponse, error) { | ||||
| 	return a.SendMessage(chat1.ChatChannel{ | ||||
| 		Name:   a.GetUsername(), | ||||
| 		Public: true, | ||||
| 	}, fmt.Sprintf(body, args...)) | ||||
| } | ||||
|  | ||||
| func (a *API) SendMessageByConvID(convID chat1.ConvIDStr, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		ConversationID: convID, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| // SendMessageByTlfName sends a message on the given TLF name | ||||
| func (a *API) SendMessageByTlfName(tlfName string, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: chat1.ChatChannel{ | ||||
| 			Name: tlfName, | ||||
| 		}, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendMessageByTeamName(teamName string, inChannel *string, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	channel := "general" | ||||
| 	if inChannel != nil { | ||||
| 		channel = *inChannel | ||||
| 	} | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: chat1.ChatChannel{ | ||||
| 			MembersType: "team", | ||||
| 			Name:        teamName, | ||||
| 			TopicName:   channel, | ||||
| 		}, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendReply(channel chat1.ChatChannel, replyTo *chat1.MessageID, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: channel, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 		ReplyTo: replyTo, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendReplyByConvID(convID chat1.ConvIDStr, replyTo *chat1.MessageID, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		ConversationID: convID, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 		ReplyTo: replyTo, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendReplyByTlfName(tlfName string, replyTo *chat1.MessageID, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: chat1.ChatChannel{ | ||||
| 			Name: tlfName, | ||||
| 		}, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 		ReplyTo: replyTo, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendAttachmentByTeam(teamName string, inChannel *string, filename string, title string) (SendResponse, error) { | ||||
| 	channel := "general" | ||||
| 	if inChannel != nil { | ||||
| 		channel = *inChannel | ||||
| 	} | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "attach", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				Channel: chat1.ChatChannel{ | ||||
| 					MembersType: "team", | ||||
| 					Name:        teamName, | ||||
| 					TopicName:   channel, | ||||
| 				}, | ||||
| 				Filename: filename, | ||||
| 				Title:    title, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendAttachmentByConvID(convID chat1.ConvIDStr, filename string, title string) (SendResponse, error) { | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "attach", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				ConversationID: convID, | ||||
| 				Filename:       filename, | ||||
| 				Title:          title, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////// | ||||
| // React to chat /////////////////////////////////////// | ||||
| //////////////////////////////////////////////////////// | ||||
|  | ||||
| type reactionOptions struct { | ||||
| 	ConversationID chat1.ConvIDStr `json:"conversation_id"` | ||||
| 	Message        sendMessageBody | ||||
| 	MsgID          chat1.MessageID   `json:"message_id"` | ||||
| 	Channel        chat1.ChatChannel `json:"channel"` | ||||
| } | ||||
|  | ||||
| type reactionParams struct { | ||||
| 	Options reactionOptions | ||||
| } | ||||
|  | ||||
| type reactionArg struct { | ||||
| 	Method string | ||||
| 	Params reactionParams | ||||
| } | ||||
|  | ||||
| func newReactionArg(options reactionOptions) reactionArg { | ||||
| 	return reactionArg{ | ||||
| 		Method: "reaction", | ||||
| 		Params: reactionParams{Options: options}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) ReactByChannel(channel chat1.ChatChannel, msgID chat1.MessageID, reaction string) (SendResponse, error) { | ||||
| 	arg := newReactionArg(reactionOptions{ | ||||
| 		Message: sendMessageBody{Body: reaction}, | ||||
| 		MsgID:   msgID, | ||||
| 		Channel: channel, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) ReactByConvID(convID chat1.ConvIDStr, msgID chat1.MessageID, reaction string) (SendResponse, error) { | ||||
| 	arg := newReactionArg(reactionOptions{ | ||||
| 		Message:        sendMessageBody{Body: reaction}, | ||||
| 		MsgID:          msgID, | ||||
| 		ConversationID: convID, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) EditByConvID(convID chat1.ConvIDStr, msgID chat1.MessageID, text string) (SendResponse, error) { | ||||
| 	arg := reactionArg{ | ||||
| 		Method: "edit", | ||||
| 		Params: reactionParams{Options: reactionOptions{ | ||||
| 			Message:        sendMessageBody{Body: text}, | ||||
| 			MsgID:          msgID, | ||||
| 			ConversationID: convID, | ||||
| 		}}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////// | ||||
| // Manage channels ///////////////////////////////////// | ||||
| //////////////////////////////////////////////////////// | ||||
|  | ||||
| type ChannelsList struct { | ||||
| 	Result Result `json:"result"` | ||||
| 	Error  *Error `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type JoinChannel struct { | ||||
| 	Error  *Error         `json:"error,omitempty"` | ||||
| 	Result chat1.EmptyRes `json:"result"` | ||||
| } | ||||
|  | ||||
| type LeaveChannel struct { | ||||
| 	Error  *Error         `json:"error,omitempty"` | ||||
| 	Result chat1.EmptyRes `json:"result"` | ||||
| } | ||||
|  | ||||
| func (a *API) ListChannels(teamName string) ([]string, error) { | ||||
| 	teamNameEscaped, err := json.Marshal(teamName) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "listconvsonname", "params": {"options": {"topic_type": "CHAT", "members_type": "team", "name": %s}}}`, teamNameEscaped) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var channelsList ChannelsList | ||||
| 	if err := json.Unmarshal(output, &channelsList); err != nil { | ||||
| 		return nil, err | ||||
| 	} else if channelsList.Error != nil { | ||||
| 		return nil, errors.New(channelsList.Error.Message) | ||||
| 	} | ||||
|  | ||||
| 	var channels []string | ||||
| 	for _, conv := range channelsList.Result.Convs { | ||||
| 		channels = append(channels, conv.Channel.TopicName) | ||||
| 	} | ||||
| 	return channels, nil | ||||
| } | ||||
|  | ||||
| func (a *API) JoinChannel(teamName string, channelName string) (chat1.EmptyRes, error) { | ||||
| 	empty := chat1.EmptyRes{} | ||||
|  | ||||
| 	teamNameEscaped, err := json.Marshal(teamName) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
| 	channelNameEscaped, err := json.Marshal(channelName) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "join", "params": {"options": {"channel": {"name": %s, "members_type": "team", "topic_name": %s}}}}`, | ||||
| 		teamNameEscaped, channelNameEscaped) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
|  | ||||
| 	joinChannel := JoinChannel{} | ||||
| 	err = json.Unmarshal(output, &joinChannel) | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to parse output from keybase team api: %v", err) | ||||
| 	} else if joinChannel.Error != nil { | ||||
| 		return empty, errors.New(joinChannel.Error.Message) | ||||
| 	} | ||||
|  | ||||
| 	return joinChannel.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) LeaveChannel(teamName string, channelName string) (chat1.EmptyRes, error) { | ||||
| 	empty := chat1.EmptyRes{} | ||||
|  | ||||
| 	teamNameEscaped, err := json.Marshal(teamName) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
| 	channelNameEscaped, err := json.Marshal(channelName) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "leave", "params": {"options": {"channel": {"name": %s, "members_type": "team", "topic_name": %s}}}}`, | ||||
| 		teamNameEscaped, channelNameEscaped) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
|  | ||||
| 	leaveChannel := LeaveChannel{} | ||||
| 	err = json.Unmarshal(output, &leaveChannel) | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to parse output from keybase team api: %v", err) | ||||
| 	} else if leaveChannel.Error != nil { | ||||
| 		return empty, errors.New(leaveChannel.Error.Message) | ||||
| 	} | ||||
|  | ||||
| 	return leaveChannel.Result, nil | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////// | ||||
| // Send lumens in chat ///////////////////////////////// | ||||
| //////////////////////////////////////////////////////// | ||||
|  | ||||
| func (a *API) InChatSend(channel chat1.ChatChannel, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: channel, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 		ConfirmLumenSend: true, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) InChatSendByConvID(convID chat1.ConvIDStr, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		ConversationID: convID, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 		ConfirmLumenSend: true, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) InChatSendByTlfName(tlfName string, body string, args ...interface{}) (SendResponse, error) { | ||||
| 	arg := newSendArg(sendMessageOptions{ | ||||
| 		Channel: chat1.ChatChannel{ | ||||
| 			Name: tlfName, | ||||
| 		}, | ||||
| 		Message: sendMessageBody{ | ||||
| 			Body: fmt.Sprintf(body, args...), | ||||
| 		}, | ||||
| 		ConfirmLumenSend: true, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////// | ||||
| // Misc commands /////////////////////////////////////// | ||||
| //////////////////////////////////////////////////////// | ||||
|  | ||||
| type Advertisement struct { | ||||
| 	Alias          string `json:"alias,omitempty"` | ||||
| 	Advertisements []chat1.AdvertiseCommandAPIParam | ||||
| } | ||||
|  | ||||
| type ListCommandsResponse struct { | ||||
| 	Result struct { | ||||
| 		Commands []chat1.UserBotCommandOutput `json:"commands"` | ||||
| 	} `json:"result"` | ||||
| 	Error *Error `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type advertiseCmdsParams struct { | ||||
| 	Options Advertisement | ||||
| } | ||||
|  | ||||
| type advertiseCmdsMsgArg struct { | ||||
| 	Method string | ||||
| 	Params advertiseCmdsParams | ||||
| } | ||||
|  | ||||
| func newAdvertiseCmdsMsgArg(ad Advertisement) advertiseCmdsMsgArg { | ||||
| 	return advertiseCmdsMsgArg{ | ||||
| 		Method: "advertisecommands", | ||||
| 		Params: advertiseCmdsParams{ | ||||
| 			Options: ad, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) AdvertiseCommands(ad Advertisement) (SendResponse, error) { | ||||
| 	return a.doSend(newAdvertiseCmdsMsgArg(ad)) | ||||
| } | ||||
|  | ||||
| type clearCmdsOptions struct { | ||||
| 	Filter *chat1.ClearCommandAPIParam `json:"filter"` | ||||
| } | ||||
|  | ||||
| type clearCmdsParams struct { | ||||
| 	Options clearCmdsOptions `json:"options"` | ||||
| } | ||||
|  | ||||
| type clearCmdsArg struct { | ||||
| 	Method string          `json:"method"` | ||||
| 	Params clearCmdsParams `json:"params,omitempty"` | ||||
| } | ||||
|  | ||||
| func (a *API) ClearCommands(filter *chat1.ClearCommandAPIParam) error { | ||||
| 	_, err := a.doSend(clearCmdsArg{ | ||||
| 		Method: "clearcommands", | ||||
| 		Params: clearCmdsParams{ | ||||
| 			Options: clearCmdsOptions{ | ||||
| 				Filter: filter, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| type listCmdsOptions struct { | ||||
| 	Channel        chat1.ChatChannel `json:"channel,omitempty"` | ||||
| 	ConversationID chat1.ConvIDStr   `json:"conversation_id,omitempty"` | ||||
| } | ||||
|  | ||||
| type listCmdsParams struct { | ||||
| 	Options listCmdsOptions | ||||
| } | ||||
|  | ||||
| type listCmdsArg struct { | ||||
| 	Method string | ||||
| 	Params listCmdsParams | ||||
| } | ||||
|  | ||||
| func newListCmdsArg(options listCmdsOptions) listCmdsArg { | ||||
| 	return listCmdsArg{ | ||||
| 		Method: "listcommands", | ||||
| 		Params: listCmdsParams{ | ||||
| 			Options: options, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) ListCommands(channel chat1.ChatChannel) ([]chat1.UserBotCommandOutput, error) { | ||||
| 	arg := newListCmdsArg(listCmdsOptions{ | ||||
| 		Channel: channel, | ||||
| 	}) | ||||
| 	return a.listCommands(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) ListCommandsByConvID(convID chat1.ConvIDStr) ([]chat1.UserBotCommandOutput, error) { | ||||
| 	arg := newListCmdsArg(listCmdsOptions{ | ||||
| 		ConversationID: convID, | ||||
| 	}) | ||||
| 	return a.listCommands(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) listCommands(arg listCmdsArg) ([]chat1.UserBotCommandOutput, error) { | ||||
| 	bArg, err := json.Marshal(arg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	output, err := a.doFetch(string(bArg)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var res ListCommandsResponse | ||||
| 	if err := json.Unmarshal(output, &res); err != nil { | ||||
| 		return nil, err | ||||
| 	} else if res.Error != nil { | ||||
| 		return nil, errors.New(res.Error.Message) | ||||
| 	} | ||||
| 	return res.Result.Commands, nil | ||||
| } | ||||
|  | ||||
| type listMembersOptions struct { | ||||
| 	Channel        chat1.ChatChannel `json:"channel,omitempty"` | ||||
| 	ConversationID chat1.ConvIDStr   `json:"conversation_id,omitempty"` | ||||
| } | ||||
|  | ||||
| type listMembersParams struct { | ||||
| 	Options listMembersOptions | ||||
| } | ||||
|  | ||||
| type listMembersArg struct { | ||||
| 	Method string | ||||
| 	Params listMembersParams | ||||
| } | ||||
|  | ||||
| func newListMembersArg(options listMembersOptions) listMembersArg { | ||||
| 	return listMembersArg{ | ||||
| 		Method: "listmembers", | ||||
| 		Params: listMembersParams{ | ||||
| 			Options: options, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) ListMembers(channel chat1.ChatChannel) (keybase1.TeamMembersDetails, error) { | ||||
| 	arg := newListMembersArg(listMembersOptions{ | ||||
| 		Channel: channel, | ||||
| 	}) | ||||
| 	return a.listMembers(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) ListMembersByConvID(conversationID chat1.ConvIDStr) (keybase1.TeamMembersDetails, error) { | ||||
| 	arg := newListMembersArg(listMembersOptions{ | ||||
| 		ConversationID: conversationID, | ||||
| 	}) | ||||
| 	return a.listMembers(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) listMembers(arg listMembersArg) (res keybase1.TeamMembersDetails, err error) { | ||||
| 	bArg, err := json.Marshal(arg) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	output, err := a.doFetch(string(bArg)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	members := ListTeamMembers{} | ||||
| 	err = json.Unmarshal(output, &members) | ||||
| 	if err != nil { | ||||
| 		return res, UnmarshalError{err} | ||||
| 	} | ||||
| 	if members.Error.Message != "" { | ||||
| 		return res, members.Error | ||||
| 	} | ||||
| 	return members.Result.Members, nil | ||||
| } | ||||
|  | ||||
| type GetMessagesResult struct { | ||||
| 	Result struct { | ||||
| 		Messages []chat1.Message `json:"messages"` | ||||
| 	} `json:"result"` | ||||
| 	Error *Error `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type getMessagesOptions struct { | ||||
| 	Channel        chat1.ChatChannel `json:"channel,omitempty"` | ||||
| 	ConversationID chat1.ConvIDStr   `json:"conversation_id,omitempty"` | ||||
| 	MessageIDs     []chat1.MessageID `json:"message_ids,omitempty"` | ||||
| } | ||||
|  | ||||
| type getMessagesParams struct { | ||||
| 	Options getMessagesOptions | ||||
| } | ||||
|  | ||||
| type getMessagesArg struct { | ||||
| 	Method string | ||||
| 	Params getMessagesParams | ||||
| } | ||||
|  | ||||
| func newGetMessagesArg(options getMessagesOptions) getMessagesArg { | ||||
| 	return getMessagesArg{ | ||||
| 		Method: "get", | ||||
| 		Params: getMessagesParams{ | ||||
| 			Options: options, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) GetMessages(channel chat1.ChatChannel, msgIDs []chat1.MessageID) ([]chat1.Message, error) { | ||||
| 	arg := newGetMessagesArg(getMessagesOptions{ | ||||
| 		Channel:    channel, | ||||
| 		MessageIDs: msgIDs, | ||||
| 	}) | ||||
| 	return a.getMessages(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) GetMessagesByConvID(conversationID chat1.ConvIDStr, msgIDs []chat1.MessageID) ([]chat1.Message, error) { | ||||
| 	arg := newGetMessagesArg(getMessagesOptions{ | ||||
| 		ConversationID: conversationID, | ||||
| 		MessageIDs:     msgIDs, | ||||
| 	}) | ||||
| 	return a.getMessages(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) getMessages(arg getMessagesArg) ([]chat1.Message, error) { | ||||
| 	bArg, err := json.Marshal(arg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	output, err := a.doFetch(string(bArg)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var res GetMessagesResult | ||||
| 	err = json.Unmarshal(output, &res) | ||||
| 	if err != nil { | ||||
| 		return nil, UnmarshalError{err} | ||||
| 	} | ||||
| 	if res.Error != nil { | ||||
| 		return nil, res.Error | ||||
| 	} | ||||
| 	return res.Result.Messages, nil | ||||
| } | ||||
							
								
								
									
										41
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| package kbchat | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| type ErrorCode int | ||||
|  | ||||
| var errAPIDisconnected = errors.New("chat API disconnected") | ||||
|  | ||||
| const ( | ||||
| 	RevisionErrorCode          ErrorCode = 2760 | ||||
| 	DeleteNonExistentErrorCode ErrorCode = 2762 | ||||
| ) | ||||
|  | ||||
| // Error is for unmarshaling CLI json responses | ||||
| type Error struct { | ||||
| 	Code    ErrorCode `json:"code"` | ||||
| 	Message string    `json:"message"` | ||||
| } | ||||
|  | ||||
| func (e Error) Error() string { | ||||
| 	return fmt.Sprintf("received error response from keybase api: %s", e.Message) | ||||
| } | ||||
|  | ||||
| type APIError struct { | ||||
| 	err error | ||||
| } | ||||
|  | ||||
| func (e APIError) Error() string { | ||||
| 	return fmt.Sprintf("failed to call keybase api: %v", e.err) | ||||
| } | ||||
|  | ||||
| type UnmarshalError struct { | ||||
| 	err error | ||||
| } | ||||
|  | ||||
| func (e UnmarshalError) Error() string { | ||||
| 	return fmt.Sprintf("failed to parse output from keybase api: %v", e.err) | ||||
| } | ||||
							
								
								
									
										767
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										767
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,59 +6,121 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/keybase1" | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/stellar1" | ||||
| ) | ||||
|  | ||||
| // API is the main object used for communicating with the Keybase JSON API | ||||
| type API struct { | ||||
| 	sync.Mutex | ||||
| 	apiInput  io.Writer | ||||
| 	apiOutput *bufio.Reader | ||||
| 	apiCmd    *exec.Cmd | ||||
| 	username  string | ||||
| 	runOpts   RunOptions | ||||
| // SubscriptionMessage contains a message and conversation object | ||||
| type SubscriptionMessage struct { | ||||
| 	Message      chat1.MsgSummary | ||||
| 	Conversation chat1.ConvSummary | ||||
| } | ||||
|  | ||||
| func getUsername(runOpts RunOptions) (username string, err error) { | ||||
| 	p := runOpts.Command("status") | ||||
| 	output, err := p.StdoutPipe() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if err = p.Start(); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| type SubscriptionConversation struct { | ||||
| 	Conversation chat1.ConvSummary | ||||
| } | ||||
|  | ||||
| 	doneCh := make(chan error) | ||||
| 	go func() { | ||||
| 		scanner := bufio.NewScanner(output) | ||||
| 		if !scanner.Scan() { | ||||
| 			doneCh <- errors.New("unable to find Keybase username") | ||||
| 			return | ||||
| 		} | ||||
| 		toks := strings.Fields(scanner.Text()) | ||||
| 		if len(toks) != 2 { | ||||
| 			doneCh <- errors.New("invalid Keybase username output") | ||||
| 			return | ||||
| 		} | ||||
| 		username = toks[1] | ||||
| 		doneCh <- nil | ||||
| 	}() | ||||
| type SubscriptionWalletEvent struct { | ||||
| 	Payment stellar1.PaymentDetailsLocal | ||||
| } | ||||
|  | ||||
| // Subscription has methods to control the background message fetcher loop | ||||
| type Subscription struct { | ||||
| 	*DebugOutput | ||||
| 	sync.Mutex | ||||
|  | ||||
| 	newMsgsCh   chan SubscriptionMessage | ||||
| 	newConvsCh  chan SubscriptionConversation | ||||
| 	newWalletCh chan SubscriptionWalletEvent | ||||
| 	errorCh     chan error | ||||
| 	running     bool | ||||
| 	shutdownCh  chan struct{} | ||||
| } | ||||
|  | ||||
| func NewSubscription() *Subscription { | ||||
| 	newMsgsCh := make(chan SubscriptionMessage, 100) | ||||
| 	newConvsCh := make(chan SubscriptionConversation, 100) | ||||
| 	newWalletCh := make(chan SubscriptionWalletEvent, 100) | ||||
| 	errorCh := make(chan error, 100) | ||||
| 	shutdownCh := make(chan struct{}) | ||||
| 	return &Subscription{ | ||||
| 		DebugOutput: NewDebugOutput("Subscription"), | ||||
| 		newMsgsCh:   newMsgsCh, | ||||
| 		newConvsCh:  newConvsCh, | ||||
| 		newWalletCh: newWalletCh, | ||||
| 		shutdownCh:  shutdownCh, | ||||
| 		errorCh:     errorCh, | ||||
| 		running:     true, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Read blocks until a new message arrives | ||||
| func (m *Subscription) Read() (msg SubscriptionMessage, err error) { | ||||
| 	defer m.Trace(&err, "Read")() | ||||
| 	select { | ||||
| 	case err = <-doneCh: | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 	case <-time.After(5 * time.Second): | ||||
| 		return "", errors.New("unable to run Keybase command") | ||||
| 	case msg = <-m.newMsgsCh: | ||||
| 		return msg, nil | ||||
| 	case err = <-m.errorCh: | ||||
| 		return SubscriptionMessage{}, err | ||||
| 	case <-m.shutdownCh: | ||||
| 		return SubscriptionMessage{}, errors.New("Subscription shutdown") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 	return username, nil | ||||
| func (m *Subscription) ReadNewConvs() (conv SubscriptionConversation, err error) { | ||||
| 	defer m.Trace(&err, "ReadNewConvs")() | ||||
| 	select { | ||||
| 	case conv = <-m.newConvsCh: | ||||
| 		return conv, nil | ||||
| 	case err = <-m.errorCh: | ||||
| 		return SubscriptionConversation{}, err | ||||
| 	case <-m.shutdownCh: | ||||
| 		return SubscriptionConversation{}, errors.New("Subscription shutdown") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Read blocks until a new message arrives | ||||
| func (m *Subscription) ReadWallet() (msg SubscriptionWalletEvent, err error) { | ||||
| 	defer m.Trace(&err, "ReadWallet")() | ||||
| 	select { | ||||
| 	case msg = <-m.newWalletCh: | ||||
| 		return msg, nil | ||||
| 	case err = <-m.errorCh: | ||||
| 		return SubscriptionWalletEvent{}, err | ||||
| 	case <-m.shutdownCh: | ||||
| 		return SubscriptionWalletEvent{}, errors.New("Subscription shutdown") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Shutdown terminates the background process | ||||
| func (m *Subscription) Shutdown() { | ||||
| 	defer m.Trace(nil, "Shutdown")() | ||||
| 	m.Lock() | ||||
| 	defer m.Unlock() | ||||
| 	if m.running { | ||||
| 		close(m.shutdownCh) | ||||
| 		m.running = false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type ListenOptions struct { | ||||
| 	Wallet bool | ||||
| 	Convs  bool | ||||
| } | ||||
|  | ||||
| type PaymentHolder struct { | ||||
| 	Payment stellar1.PaymentDetailsLocal `json:"notification"` | ||||
| } | ||||
|  | ||||
| type TypeHolder struct { | ||||
| 	Type string `json:"type"` | ||||
| } | ||||
|  | ||||
| type OneshotOptions struct { | ||||
| @@ -71,6 +133,10 @@ type RunOptions struct { | ||||
| 	HomeDir         string | ||||
| 	Oneshot         *OneshotOptions | ||||
| 	StartService    bool | ||||
| 	// Have the bot send/receive typing notifications | ||||
| 	EnableTyping bool | ||||
| 	// Disable bot lite mode | ||||
| 	DisableBotLiteMode bool | ||||
| } | ||||
|  | ||||
| func (r RunOptions) Location() string { | ||||
| @@ -90,18 +156,101 @@ func (r RunOptions) Command(args ...string) *exec.Cmd { | ||||
| } | ||||
|  | ||||
| // Start fires up the Keybase JSON API in stdin/stdout mode | ||||
| func Start(runOpts RunOptions) (*API, error) { | ||||
| 	api := &API{ | ||||
| 		runOpts: runOpts, | ||||
| 	} | ||||
| func Start(runOpts RunOptions, opts ...func(*API)) (*API, error) { | ||||
| 	api := NewAPI(runOpts, opts...) | ||||
| 	if err := api.startPipes(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return api, nil | ||||
| } | ||||
|  | ||||
| // API is the main object used for communicating with the Keybase JSON API | ||||
| type API struct { | ||||
| 	sync.Mutex | ||||
| 	*DebugOutput | ||||
| 	apiInput      io.Writer | ||||
| 	apiOutput     *bufio.Reader | ||||
| 	apiCmd        *exec.Cmd | ||||
| 	username      string | ||||
| 	runOpts       RunOptions | ||||
| 	subscriptions []*Subscription | ||||
| 	Timeout       time.Duration | ||||
| 	LogSendBytes  int | ||||
| } | ||||
|  | ||||
| func CustomTimeout(timeout time.Duration) func(*API) { | ||||
| 	return func(a *API) { | ||||
| 		a.Timeout = timeout | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func NewAPI(runOpts RunOptions, opts ...func(*API)) *API { | ||||
| 	api := &API{ | ||||
| 		DebugOutput:  NewDebugOutput("API"), | ||||
| 		runOpts:      runOpts, | ||||
| 		Timeout:      5 * time.Second, | ||||
| 		LogSendBytes: 1024 * 1024 * 5, // request 5MB so we don't get killed | ||||
| 	} | ||||
| 	for _, opt := range opts { | ||||
| 		opt(api) | ||||
| 	} | ||||
| 	return api | ||||
| } | ||||
|  | ||||
| func (a *API) Command(args ...string) *exec.Cmd { | ||||
| 	return a.runOpts.Command(args...) | ||||
| } | ||||
|  | ||||
| func (a *API) getUsername(runOpts RunOptions) (username string, err error) { | ||||
| 	p := runOpts.Command("whoami", "-json") | ||||
| 	output, err := p.StdoutPipe() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	p.ExtraFiles = []*os.File{output.(*os.File)} | ||||
| 	if err = p.Start(); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	doneCh := make(chan error) | ||||
| 	go func() { | ||||
| 		defer func() { close(doneCh) }() | ||||
| 		statusJSON, err := ioutil.ReadAll(output) | ||||
| 		if err != nil { | ||||
| 			doneCh <- fmt.Errorf("error reading whoami output: %v", err) | ||||
| 			return | ||||
| 		} | ||||
| 		var status keybase1.CurrentStatus | ||||
| 		if err := json.Unmarshal(statusJSON, &status); err != nil { | ||||
| 			doneCh <- fmt.Errorf("invalid whoami JSON %q: %v", statusJSON, err) | ||||
| 			return | ||||
| 		} | ||||
| 		if status.LoggedIn && status.User != nil { | ||||
| 			username = status.User.Username | ||||
| 			doneCh <- nil | ||||
| 		} else { | ||||
| 			doneCh <- fmt.Errorf("unable to authenticate to keybase service: logged in: %v user: %+v", status.LoggedIn, status.User) | ||||
| 		} | ||||
| 		// Cleanup the command | ||||
| 		if err := p.Wait(); err != nil { | ||||
| 			a.Debug("unable to wait for cmd: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	select { | ||||
| 	case err = <-doneCh: | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 	case <-time.After(a.Timeout): | ||||
| 		return "", errors.New("unable to run Keybase command") | ||||
| 	} | ||||
|  | ||||
| 	return username, nil | ||||
| } | ||||
|  | ||||
| func (a *API) auth() (string, error) { | ||||
| 	username, err := getUsername(a.runOpts) | ||||
| 	username, err := a.getUsername(a.runOpts) | ||||
| 	if err == nil { | ||||
| 		return username, nil | ||||
| 	} | ||||
| @@ -132,17 +281,28 @@ func (a *API) startPipes() (err error) { | ||||
| 	a.Lock() | ||||
| 	defer a.Unlock() | ||||
| 	if a.apiCmd != nil { | ||||
| 		a.apiCmd.Process.Kill() | ||||
| 		if err := a.apiCmd.Process.Kill(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	a.apiCmd = nil | ||||
|  | ||||
| 	if a.runOpts.StartService { | ||||
| 		a.runOpts.Command("service").Start() | ||||
| 		args := []string{fmt.Sprintf("-enable-bot-lite-mode=%v", a.runOpts.DisableBotLiteMode), "service"} | ||||
| 		if err := a.runOpts.Command(args...).Start(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if a.username, err = a.auth(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	cmd := a.runOpts.Command("chat", "notification-settings", fmt.Sprintf("-disable-typing=%v", !a.runOpts.EnableTyping)) | ||||
| 	if err = cmd.Run(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	a.apiCmd = a.runOpts.Command("chat", "api") | ||||
| 	if a.apiInput, err = a.apiCmd.StdinPipe(); err != nil { | ||||
| 		return err | ||||
| @@ -151,6 +311,7 @@ func (a *API) startPipes() (err error) { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	a.apiCmd.ExtraFiles = []*os.File{output.(*os.File)} | ||||
| 	if err := a.apiCmd.Start(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -158,8 +319,6 @@ func (a *API) startPipes() (err error) { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| var errAPIDisconnected = errors.New("chat API disconnected") | ||||
|  | ||||
| func (a *API) getAPIPipesLocked() (io.Writer, *bufio.Reader, error) { | ||||
| 	// this should only be called inside a lock | ||||
| 	if a.apiCmd == nil { | ||||
| @@ -168,79 +327,17 @@ func (a *API) getAPIPipesLocked() (io.Writer, *bufio.Reader, error) { | ||||
| 	return a.apiInput, a.apiOutput, nil | ||||
| } | ||||
|  | ||||
| // GetConversations reads all conversations from the current user's inbox. | ||||
| func (a *API) GetConversations(unreadOnly bool) ([]Conversation, error) { | ||||
| 	apiInput := fmt.Sprintf(`{"method":"list", "params": { "options": { "unread_only": %v}}}`, unreadOnly) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var inbox Inbox | ||||
| 	if err := json.Unmarshal(output, &inbox); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return inbox.Result.Convs, nil | ||||
| func (a *API) GetUsername() string { | ||||
| 	return a.username | ||||
| } | ||||
|  | ||||
| // GetTextMessages fetches all text messages from a given channel. Optionally can filter | ||||
| // ont unread status. | ||||
| func (a *API) GetTextMessages(channel Channel, unreadOnly bool) ([]Message, error) { | ||||
| 	channelBytes, err := json.Marshal(channel) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "read", "params": {"options": {"channel": %s}}}`, string(channelBytes)) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var thread Thread | ||||
|  | ||||
| 	if err := json.Unmarshal(output, &thread); err != nil { | ||||
| 		return nil, fmt.Errorf("unable to decode thread: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	var res []Message | ||||
| 	for _, msg := range thread.Result.Messages { | ||||
| 		if msg.Msg.Content.Type == "text" { | ||||
| 			res = append(res, msg.Msg) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return res, nil | ||||
| } | ||||
|  | ||||
| type sendMessageBody struct { | ||||
| 	Body string | ||||
| } | ||||
|  | ||||
| type sendMessageOptions struct { | ||||
| 	Channel        Channel         `json:"channel,omitempty"` | ||||
| 	ConversationID string          `json:"conversation_id,omitempty"` | ||||
| 	Message        sendMessageBody `json:",omitempty"` | ||||
| 	Filename       string          `json:"filename,omitempty"` | ||||
| 	Title          string          `json:"title,omitempty"` | ||||
| 	MsgID          int             `json:"message_id,omitempty"` | ||||
| } | ||||
|  | ||||
| type sendMessageParams struct { | ||||
| 	Options sendMessageOptions | ||||
| } | ||||
|  | ||||
| type sendMessageArg struct { | ||||
| 	Method string | ||||
| 	Params sendMessageParams | ||||
| } | ||||
|  | ||||
| func (a *API) doSend(arg interface{}) (response SendResponse, err error) { | ||||
| func (a *API) doSend(arg interface{}) (resp SendResponse, err error) { | ||||
| 	a.Lock() | ||||
| 	defer a.Unlock() | ||||
|  | ||||
| 	bArg, err := json.Marshal(arg) | ||||
| 	if err != nil { | ||||
| 		return SendResponse{}, err | ||||
| 		return SendResponse{}, fmt.Errorf("unable to send arg: %+v: %v", arg, err) | ||||
| 	} | ||||
| 	input, output, err := a.getAPIPipesLocked() | ||||
| 	if err != nil { | ||||
| @@ -253,10 +350,12 @@ func (a *API) doSend(arg interface{}) (response SendResponse, err error) { | ||||
| 	if err != nil { | ||||
| 		return SendResponse{}, err | ||||
| 	} | ||||
| 	if err := json.Unmarshal(responseRaw, &response); err != nil { | ||||
| 		return SendResponse{}, fmt.Errorf("failed to decode API response: %s", err) | ||||
| 	if err := json.Unmarshal(responseRaw, &resp); err != nil { | ||||
| 		return resp, fmt.Errorf("failed to decode API response: %v %v", responseRaw, err) | ||||
| 	} else if resp.Error != nil { | ||||
| 		return resp, errors.New(resp.Error.Message) | ||||
| 	} | ||||
| 	return response, nil | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func (a *API) doFetch(apiInput string) ([]byte, error) { | ||||
| @@ -278,286 +377,114 @@ func (a *API) doFetch(apiInput string) ([]byte, error) { | ||||
| 	return byteOutput, nil | ||||
| } | ||||
|  | ||||
| func (a *API) SendMessage(channel Channel, body string) (SendResponse, error) { | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "send", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				Channel: channel, | ||||
| 				Message: sendMessageBody{ | ||||
| 					Body: body, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendMessageByConvID(convID string, body string) (SendResponse, error) { | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "send", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				ConversationID: convID, | ||||
| 				Message: sendMessageBody{ | ||||
| 					Body: body, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| // SendMessageByTlfName sends a message on the given TLF name | ||||
| func (a *API) SendMessageByTlfName(tlfName string, body string) (SendResponse, error) { | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "send", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				Channel: Channel{ | ||||
| 					Name: tlfName, | ||||
| 				}, | ||||
| 				Message: sendMessageBody{ | ||||
| 					Body: body, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendMessageByTeamName(teamName string, body string, inChannel *string) (SendResponse, error) { | ||||
| 	channel := "general" | ||||
| 	if inChannel != nil { | ||||
| 		channel = *inChannel | ||||
| 	} | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "send", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				Channel: Channel{ | ||||
| 					MembersType: "team", | ||||
| 					Name:        teamName, | ||||
| 					TopicName:   channel, | ||||
| 				}, | ||||
| 				Message: sendMessageBody{ | ||||
| 					Body: body, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) SendAttachmentByTeam(teamName string, filename string, title string, inChannel *string) (SendResponse, error) { | ||||
| 	channel := "general" | ||||
| 	if inChannel != nil { | ||||
| 		channel = *inChannel | ||||
| 	} | ||||
| 	arg := sendMessageArg{ | ||||
| 		Method: "attach", | ||||
| 		Params: sendMessageParams{ | ||||
| 			Options: sendMessageOptions{ | ||||
| 				Channel: Channel{ | ||||
| 					MembersType: "team", | ||||
| 					Name:        teamName, | ||||
| 					TopicName:   channel, | ||||
| 				}, | ||||
| 				Filename: filename, | ||||
| 				Title:    title, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| type reactionOptions struct { | ||||
| 	ConversationID string `json:"conversation_id"` | ||||
| 	Message        sendMessageBody | ||||
| 	MsgID          int     `json:"message_id"` | ||||
| 	Channel        Channel `json:"channel"` | ||||
| } | ||||
|  | ||||
| type reactionParams struct { | ||||
| 	Options reactionOptions | ||||
| } | ||||
|  | ||||
| type reactionArg struct { | ||||
| 	Method string | ||||
| 	Params reactionParams | ||||
| } | ||||
|  | ||||
| func newReactionArg(options reactionOptions) reactionArg { | ||||
| 	return reactionArg{ | ||||
| 		Method: "reaction", | ||||
| 		Params: reactionParams{Options: options}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) ReactByChannel(channel Channel, msgID int, reaction string) (SendResponse, error) { | ||||
| 	arg := newReactionArg(reactionOptions{ | ||||
| 		Message: sendMessageBody{Body: reaction}, | ||||
| 		MsgID:   msgID, | ||||
| 		Channel: channel, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| func (a *API) ReactByConvID(convID string, msgID int, reaction string) (SendResponse, error) { | ||||
| 	arg := newReactionArg(reactionOptions{ | ||||
| 		Message:        sendMessageBody{Body: reaction}, | ||||
| 		MsgID:          msgID, | ||||
| 		ConversationID: convID, | ||||
| 	}) | ||||
| 	return a.doSend(arg) | ||||
| } | ||||
|  | ||||
| type advertiseParams struct { | ||||
| 	Options Advertisement | ||||
| } | ||||
|  | ||||
| type advertiseMsgArg struct { | ||||
| 	Method string | ||||
| 	Params advertiseParams | ||||
| } | ||||
|  | ||||
| func newAdvertiseMsgArg(ad Advertisement) advertiseMsgArg { | ||||
| 	return advertiseMsgArg{ | ||||
| 		Method: "advertisecommands", | ||||
| 		Params: advertiseParams{ | ||||
| 			Options: ad, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (a *API) AdvertiseCommands(ad Advertisement) (SendResponse, error) { | ||||
| 	return a.doSend(newAdvertiseMsgArg(ad)) | ||||
| } | ||||
|  | ||||
| func (a *API) Username() string { | ||||
| 	return a.username | ||||
| } | ||||
|  | ||||
| // SubscriptionMessage contains a message and conversation object | ||||
| type SubscriptionMessage struct { | ||||
| 	Message      Message | ||||
| 	Conversation Conversation | ||||
| } | ||||
|  | ||||
| type SubscriptionWalletEvent struct { | ||||
| 	Payment Payment | ||||
| } | ||||
|  | ||||
| // NewSubscription has methods to control the background message fetcher loop | ||||
| type NewSubscription struct { | ||||
| 	newMsgsCh   <-chan SubscriptionMessage | ||||
| 	newWalletCh <-chan SubscriptionWalletEvent | ||||
| 	errorCh     <-chan error | ||||
| 	shutdownCh  chan struct{} | ||||
| } | ||||
|  | ||||
| // Read blocks until a new message arrives | ||||
| func (m NewSubscription) Read() (SubscriptionMessage, error) { | ||||
| 	select { | ||||
| 	case msg := <-m.newMsgsCh: | ||||
| 		return msg, nil | ||||
| 	case err := <-m.errorCh: | ||||
| 		return SubscriptionMessage{}, err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Read blocks until a new message arrives | ||||
| func (m NewSubscription) ReadWallet() (SubscriptionWalletEvent, error) { | ||||
| 	select { | ||||
| 	case msg := <-m.newWalletCh: | ||||
| 		return msg, nil | ||||
| 	case err := <-m.errorCh: | ||||
| 		return SubscriptionWalletEvent{}, err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Shutdown terminates the background process | ||||
| func (m NewSubscription) Shutdown() { | ||||
| 	m.shutdownCh <- struct{}{} | ||||
| } | ||||
|  | ||||
| type ListenOptions struct { | ||||
| 	Wallet bool | ||||
| } | ||||
|  | ||||
| // ListenForNewTextMessages proxies to Listen without wallet events | ||||
| func (a *API) ListenForNewTextMessages() (NewSubscription, error) { | ||||
| func (a *API) ListenForNewTextMessages() (*Subscription, error) { | ||||
| 	opts := ListenOptions{Wallet: false} | ||||
| 	return a.Listen(opts) | ||||
| } | ||||
|  | ||||
| func (a *API) registerSubscription(sub *Subscription) { | ||||
| 	a.Lock() | ||||
| 	defer a.Unlock() | ||||
| 	a.subscriptions = append(a.subscriptions, sub) | ||||
| } | ||||
|  | ||||
| // Listen fires of a background loop and puts chat messages and wallet | ||||
| // events into channels | ||||
| func (a *API) Listen(opts ListenOptions) (NewSubscription, error) { | ||||
| 	newMsgCh := make(chan SubscriptionMessage, 100) | ||||
| 	newWalletCh := make(chan SubscriptionWalletEvent, 100) | ||||
| 	errorCh := make(chan error, 100) | ||||
| 	shutdownCh := make(chan struct{}) | ||||
| func (a *API) Listen(opts ListenOptions) (*Subscription, error) { | ||||
| 	done := make(chan struct{}) | ||||
|  | ||||
| 	sub := NewSubscription{ | ||||
| 		newMsgsCh:   newMsgCh, | ||||
| 		newWalletCh: newWalletCh, | ||||
| 		shutdownCh:  shutdownCh, | ||||
| 		errorCh:     errorCh, | ||||
| 	} | ||||
| 	sub := NewSubscription() | ||||
| 	a.registerSubscription(sub) | ||||
| 	pause := 2 * time.Second | ||||
| 	readScanner := func(boutput *bufio.Scanner) { | ||||
| 		defer func() { done <- struct{}{} }() | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-sub.shutdownCh: | ||||
| 				a.Debug("readScanner: received shutdown") | ||||
| 				return | ||||
| 			default: | ||||
| 			} | ||||
| 			boutput.Scan() | ||||
| 			t := boutput.Text() | ||||
| 			var typeHolder TypeHolder | ||||
| 			if err := json.Unmarshal([]byte(t), &typeHolder); err != nil { | ||||
| 				errorCh <- err | ||||
| 				sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) | ||||
| 				break | ||||
| 			} | ||||
| 			switch typeHolder.Type { | ||||
| 			case "chat": | ||||
| 				var holder MessageHolder | ||||
| 				if err := json.Unmarshal([]byte(t), &holder); err != nil { | ||||
| 					errorCh <- err | ||||
| 				var notification chat1.MsgNotification | ||||
| 				if err := json.Unmarshal([]byte(t), ¬ification); err != nil { | ||||
| 					sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) | ||||
| 					break | ||||
| 				} | ||||
| 				subscriptionMessage := SubscriptionMessage{ | ||||
| 					Message: holder.Msg, | ||||
| 					Conversation: Conversation{ | ||||
| 						ID:      holder.Msg.ConversationID, | ||||
| 						Channel: holder.Msg.Channel, | ||||
| 					}, | ||||
| 				if notification.Error != nil { | ||||
| 					a.Debug("error message received: %s", *notification.Error) | ||||
| 				} else if notification.Msg != nil { | ||||
| 					subscriptionMessage := SubscriptionMessage{ | ||||
| 						Message: *notification.Msg, | ||||
| 						Conversation: chat1.ConvSummary{ | ||||
| 							Id:      notification.Msg.ConvID, | ||||
| 							Channel: notification.Msg.Channel, | ||||
| 						}, | ||||
| 					} | ||||
| 					sub.newMsgsCh <- subscriptionMessage | ||||
| 				} | ||||
| 			case "chat_conv": | ||||
| 				var notification chat1.ConvNotification | ||||
| 				if err := json.Unmarshal([]byte(t), ¬ification); err != nil { | ||||
| 					sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) | ||||
| 					break | ||||
| 				} | ||||
| 				if notification.Error != nil { | ||||
| 					a.Debug("error message received: %s", *notification.Error) | ||||
| 				} else if notification.Conv != nil { | ||||
| 					subscriptionConv := SubscriptionConversation{ | ||||
| 						Conversation: *notification.Conv, | ||||
| 					} | ||||
| 					sub.newConvsCh <- subscriptionConv | ||||
| 				} | ||||
| 				newMsgCh <- subscriptionMessage | ||||
| 			case "wallet": | ||||
| 				var holder PaymentHolder | ||||
| 				if err := json.Unmarshal([]byte(t), &holder); err != nil { | ||||
| 					errorCh <- err | ||||
| 					sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) | ||||
| 					break | ||||
| 				} | ||||
| 				subscriptionPayment := SubscriptionWalletEvent{ | ||||
| 					Payment: holder.Payment, | ||||
| 				} | ||||
| 				newWalletCh <- subscriptionPayment | ||||
| 				subscriptionPayment := SubscriptionWalletEvent(holder) | ||||
| 				sub.newWalletCh <- subscriptionPayment | ||||
| 			default: | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		done <- struct{}{} | ||||
| 	} | ||||
|  | ||||
| 	attempts := 0 | ||||
| 	maxAttempts := 1800 | ||||
| 	maxAttempts := 30 | ||||
| 	go func() { | ||||
| 		defer func() { | ||||
| 			close(sub.newMsgsCh) | ||||
| 			close(sub.newConvsCh) | ||||
| 			close(sub.newWalletCh) | ||||
| 			close(sub.errorCh) | ||||
| 		}() | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-sub.shutdownCh: | ||||
| 				a.Debug("Listen: received shutdown") | ||||
| 				return | ||||
| 			default: | ||||
| 			} | ||||
|  | ||||
| 			if attempts >= maxAttempts { | ||||
| 				if err := a.LogSend("Listen: failed to auth, giving up"); err != nil { | ||||
| 					a.Debug("Listen: logsend failed to send: %v", err) | ||||
| 				} | ||||
| 				panic("Listen: failed to auth, giving up") | ||||
| 			} | ||||
| 			attempts++ | ||||
| 			if _, err := a.auth(); err != nil { | ||||
| 				log.Printf("Listen: failed to auth: %s", err) | ||||
| 				a.Debug("Listen: failed to auth: %s", err) | ||||
| 				time.Sleep(pause) | ||||
| 				continue | ||||
| 			} | ||||
| @@ -565,94 +492,54 @@ func (a *API) Listen(opts ListenOptions) (NewSubscription, error) { | ||||
| 			if opts.Wallet { | ||||
| 				cmdElements = append(cmdElements, "--wallet") | ||||
| 			} | ||||
| 			if opts.Convs { | ||||
| 				cmdElements = append(cmdElements, "--convs") | ||||
| 			} | ||||
| 			p := a.runOpts.Command(cmdElements...) | ||||
| 			output, err := p.StdoutPipe() | ||||
| 			if err != nil { | ||||
| 				log.Printf("Listen: failed to listen: %s", err) | ||||
| 				a.Debug("Listen: failed to listen: %s", err) | ||||
| 				time.Sleep(pause) | ||||
| 				continue | ||||
| 			} | ||||
| 			stderr, err := p.StderrPipe() | ||||
| 			if err != nil { | ||||
| 				a.Debug("Listen: failed to listen to stderr: %s", err) | ||||
| 				time.Sleep(pause) | ||||
| 				continue | ||||
| 			} | ||||
| 			p.ExtraFiles = []*os.File{stderr.(*os.File), output.(*os.File)} | ||||
| 			boutput := bufio.NewScanner(output) | ||||
| 			if err := p.Start(); err != nil { | ||||
| 				log.Printf("Listen: failed to make listen scanner: %s", err) | ||||
|  | ||||
| 				a.Debug("Listen: failed to make listen scanner: %s", err) | ||||
| 				time.Sleep(pause) | ||||
| 				continue | ||||
| 			} | ||||
| 			attempts = 0 | ||||
| 			go readScanner(boutput) | ||||
| 			<-done | ||||
| 			p.Wait() | ||||
| 			select { | ||||
| 			case <-sub.shutdownCh: | ||||
| 				a.Debug("Listen: received shutdown") | ||||
| 				return | ||||
| 			case <-done: | ||||
| 			} | ||||
| 			if err := p.Wait(); err != nil { | ||||
| 				stderrBytes, rerr := ioutil.ReadAll(stderr) | ||||
| 				if rerr != nil { | ||||
| 					stderrBytes = []byte(fmt.Sprintf("failed to get stderr: %v", rerr)) | ||||
| 				} | ||||
| 				a.Debug("Listen: failed to Wait for command, restarting pipes: %s (```%s```)", err, stderrBytes) | ||||
| 				if err := a.startPipes(); err != nil { | ||||
| 					a.Debug("Listen: failed to restart pipes: %v", err) | ||||
| 				} | ||||
| 			} | ||||
| 			time.Sleep(pause) | ||||
| 		} | ||||
| 	}() | ||||
| 	return sub, nil | ||||
| } | ||||
|  | ||||
| func (a *API) GetUsername() string { | ||||
| 	return a.username | ||||
| } | ||||
|  | ||||
| func (a *API) ListChannels(teamName string) ([]string, error) { | ||||
| 	apiInput := fmt.Sprintf(`{"method": "listconvsonname", "params": {"options": {"topic_type": "CHAT", "members_type": "team", "name": "%s"}}}`, teamName) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var channelsList ChannelsList | ||||
| 	if err := json.Unmarshal(output, &channelsList); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var channels []string | ||||
| 	for _, conv := range channelsList.Result.Convs { | ||||
| 		channels = append(channels, conv.Channel.TopicName) | ||||
| 	} | ||||
| 	return channels, nil | ||||
| } | ||||
|  | ||||
| func (a *API) JoinChannel(teamName string, channelName string) (JoinChannelResult, error) { | ||||
| 	empty := JoinChannelResult{} | ||||
|  | ||||
| 	apiInput := fmt.Sprintf(`{"method": "join", "params": {"options": {"channel": {"name": "%s", "members_type": "team", "topic_name": "%s"}}}}`, teamName, channelName) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
|  | ||||
| 	joinChannel := JoinChannel{} | ||||
| 	err = json.Unmarshal(output, &joinChannel) | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to parse output from keybase team api: %v", err) | ||||
| 	} | ||||
| 	if joinChannel.Error.Message != "" { | ||||
| 		return empty, fmt.Errorf("received error from keybase team api: %s", joinChannel.Error.Message) | ||||
| 	} | ||||
|  | ||||
| 	return joinChannel.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) LeaveChannel(teamName string, channelName string) (LeaveChannelResult, error) { | ||||
| 	empty := LeaveChannelResult{} | ||||
|  | ||||
| 	apiInput := fmt.Sprintf(`{"method": "leave", "params": {"options": {"channel": {"name": "%s", "members_type": "team", "topic_name": "%s"}}}}`, teamName, channelName) | ||||
| 	output, err := a.doFetch(apiInput) | ||||
| 	if err != nil { | ||||
| 		return empty, err | ||||
| 	} | ||||
|  | ||||
| 	leaveChannel := LeaveChannel{} | ||||
| 	err = json.Unmarshal(output, &leaveChannel) | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to parse output from keybase team api: %v", err) | ||||
| 	} | ||||
| 	if leaveChannel.Error.Message != "" { | ||||
| 		return empty, fmt.Errorf("received error from keybase team api: %s", leaveChannel.Error.Message) | ||||
| 	} | ||||
|  | ||||
| 	return leaveChannel.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) LogSend(feedback string) error { | ||||
| 	feedback = "go-keybase-chat-bot log send\n" + | ||||
| 		"username: " + a.GetUsername() + "\n" + | ||||
| @@ -662,20 +549,27 @@ func (a *API) LogSend(feedback string) error { | ||||
| 		"log", "send", | ||||
| 		"--no-confirm", | ||||
| 		"--feedback", feedback, | ||||
| 		"-n", fmt.Sprintf("%d", a.LogSendBytes), | ||||
| 	} | ||||
|  | ||||
| 	// We're determining whether the service is already running by running status | ||||
| 	// with autofork disabled. | ||||
| 	if err := a.runOpts.Command("--no-auto-fork", "status"); err != nil { | ||||
| 		// Assume that there's no service running, so log send as standalone | ||||
| 		args = append([]string{"--standalone"}, args...) | ||||
| 	} | ||||
|  | ||||
| 	return a.runOpts.Command(args...).Run() | ||||
| } | ||||
|  | ||||
| func (a *API) Shutdown() error { | ||||
| func (a *API) Shutdown() (err error) { | ||||
| 	defer a.Trace(&err, "Shutdown")() | ||||
| 	a.Lock() | ||||
| 	defer a.Unlock() | ||||
| 	for _, sub := range a.subscriptions { | ||||
| 		sub.Shutdown() | ||||
| 	} | ||||
| 	if a.apiCmd != nil { | ||||
| 		a.Debug("waiting for API command") | ||||
| 		if err := a.apiCmd.Wait(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if a.runOpts.Oneshot != nil { | ||||
| 		a.Debug("logging out") | ||||
| 		err := a.runOpts.Command("logout", "--force").Run() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| @@ -683,6 +577,7 @@ func (a *API) Shutdown() error { | ||||
| 	} | ||||
|  | ||||
| 	if a.runOpts.StartService { | ||||
| 		a.Debug("stopping service") | ||||
| 		err := a.runOpts.Command("ctl", "stop", "--shutdown").Run() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
|   | ||||
							
								
								
									
										228
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kvstore.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kvstore.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,228 @@ | ||||
| package kbchat | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/keybase1" | ||||
| ) | ||||
|  | ||||
| type kvstoreMethod string | ||||
|  | ||||
| type kvstoreOptions struct { | ||||
| 	Team       *string `json:"team"` | ||||
| 	Namespace  *string `json:"namespace,omitempty"` | ||||
| 	EntryKey   *string `json:"entryKey,omitempty"` | ||||
| 	EntryValue *string `json:"entryValue,omitempty"` | ||||
| 	Revision   *int    `json:"revision,omitempty"` | ||||
| } | ||||
|  | ||||
| type kvstoreParams struct { | ||||
| 	Options kvstoreOptions `json:"options"` | ||||
| } | ||||
|  | ||||
| type kvstoreAPIReq struct { | ||||
| 	Method kvstoreMethod `json:"method"` | ||||
| 	Params kvstoreParams `json:"params"` | ||||
| } | ||||
|  | ||||
| type GetEntryRes struct { | ||||
| 	Result keybase1.KVGetResult `json:"result"` | ||||
| 	Error  Error                `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type PutEntryRes struct { | ||||
| 	Result keybase1.KVPutResult `json:"result"` | ||||
| 	Error  Error                `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type DeleteEntryRes struct { | ||||
| 	Result keybase1.KVDeleteEntryResult `json:"result"` | ||||
| 	Error  Error                        `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type ListNamespacesRes struct { | ||||
| 	Result keybase1.KVListNamespaceResult `json:"result"` | ||||
| 	Error  Error                          `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type ListEntryKeysRes struct { | ||||
| 	Result keybase1.KVListEntryResult `json:"result"` | ||||
| 	Error  Error                      `json:"error,omitempty"` | ||||
| } | ||||
|  | ||||
| type KVStoreAPI interface { | ||||
| 	PutEntry(teamName *string, namespace string, entryKey string, entryValue string) (keybase1.KVPutResult, error) | ||||
| 	PutEntryWithRevision(teamName *string, namespace string, entryKey string, entryValue string, revision int) (keybase1.KVPutResult, error) | ||||
| 	DeleteEntry(teamName *string, namespace string, entryKey string) (keybase1.KVDeleteEntryResult, error) | ||||
| 	DeleteEntryWithRevision(teamName *string, namespace string, entryKey string, revision int) (keybase1.KVDeleteEntryResult, error) | ||||
| 	GetEntry(teamName *string, namespace string, entryKey string) (keybase1.KVGetResult, error) | ||||
| 	ListNamespaces(teamName *string) (keybase1.KVListNamespaceResult, error) | ||||
| 	ListEntryKeys(teamName *string, namespace string) (keybase1.KVListEntryResult, error) | ||||
| } | ||||
|  | ||||
| func (a *API) PutEntry(teamName *string, namespace string, entryKey string, entryValue string) (result keybase1.KVPutResult, err error) { | ||||
| 	return a.PutEntryWithRevision(teamName, namespace, entryKey, entryValue, 0) | ||||
| } | ||||
|  | ||||
| func (a *API) PutEntryWithRevision(teamName *string, namespace string, entryKey string, entryValue string, revision int) (result keybase1.KVPutResult, err error) { | ||||
|  | ||||
| 	opts := kvstoreOptions{ | ||||
| 		Team:       teamName, | ||||
| 		Namespace:  &namespace, | ||||
| 		EntryKey:   &entryKey, | ||||
| 		EntryValue: &entryValue, | ||||
| 	} | ||||
| 	if revision != 0 { | ||||
| 		opts.Revision = &revision | ||||
| 	} | ||||
| 	args := kvstoreAPIReq{Method: "put", Params: kvstoreParams{Options: opts}} | ||||
| 	apiInput, err := json.Marshal(args) | ||||
| 	if err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
|  | ||||
| 	cmd := a.runOpts.Command("kvstore", "api") | ||||
| 	cmd.Stdin = strings.NewReader(string(apiInput)) | ||||
| 	bytes, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return result, APIError{err} | ||||
| 	} | ||||
|  | ||||
| 	entry := PutEntryRes{} | ||||
| 	err = json.Unmarshal(bytes, &entry) | ||||
| 	if err != nil { | ||||
| 		return result, UnmarshalError{err} | ||||
| 	} | ||||
| 	if entry.Error.Message != "" { | ||||
| 		return result, entry.Error | ||||
| 	} | ||||
| 	return entry.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) DeleteEntry(teamName *string, namespace string, entryKey string) (result keybase1.KVDeleteEntryResult, err error) { | ||||
| 	return a.DeleteEntryWithRevision(teamName, namespace, entryKey, 0) | ||||
| } | ||||
|  | ||||
| func (a *API) DeleteEntryWithRevision(teamName *string, namespace string, entryKey string, revision int) (result keybase1.KVDeleteEntryResult, err error) { | ||||
|  | ||||
| 	opts := kvstoreOptions{ | ||||
| 		Team:      teamName, | ||||
| 		Namespace: &namespace, | ||||
| 		EntryKey:  &entryKey, | ||||
| 	} | ||||
| 	if revision != 0 { | ||||
| 		opts.Revision = &revision | ||||
| 	} | ||||
| 	args := kvstoreAPIReq{Method: "del", Params: kvstoreParams{Options: opts}} | ||||
| 	apiInput, err := json.Marshal(args) | ||||
| 	if err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
|  | ||||
| 	cmd := a.runOpts.Command("kvstore", "api") | ||||
| 	cmd.Stdin = strings.NewReader(string(apiInput)) | ||||
| 	bytes, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return result, APIError{err} | ||||
| 	} | ||||
|  | ||||
| 	entry := DeleteEntryRes{} | ||||
| 	err = json.Unmarshal(bytes, &entry) | ||||
| 	if err != nil { | ||||
| 		return result, UnmarshalError{err} | ||||
| 	} | ||||
| 	if entry.Error.Message != "" { | ||||
| 		return result, entry.Error | ||||
| 	} | ||||
| 	return entry.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) GetEntry(teamName *string, namespace string, entryKey string) (result keybase1.KVGetResult, err error) { | ||||
|  | ||||
| 	opts := kvstoreOptions{ | ||||
| 		Team:      teamName, | ||||
| 		Namespace: &namespace, | ||||
| 		EntryKey:  &entryKey, | ||||
| 	} | ||||
| 	args := kvstoreAPIReq{Method: "get", Params: kvstoreParams{Options: opts}} | ||||
| 	apiInput, err := json.Marshal(args) | ||||
| 	if err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
| 	cmd := a.runOpts.Command("kvstore", "api") | ||||
| 	cmd.Stdin = strings.NewReader(string(apiInput)) | ||||
| 	bytes, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return result, APIError{err} | ||||
| 	} | ||||
|  | ||||
| 	entry := GetEntryRes{} | ||||
| 	err = json.Unmarshal(bytes, &entry) | ||||
| 	if err != nil { | ||||
| 		return result, UnmarshalError{err} | ||||
| 	} | ||||
| 	if entry.Error.Message != "" { | ||||
| 		return result, entry.Error | ||||
| 	} | ||||
| 	return entry.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) ListNamespaces(teamName *string) (result keybase1.KVListNamespaceResult, err error) { | ||||
|  | ||||
| 	opts := kvstoreOptions{ | ||||
| 		Team: teamName, | ||||
| 	} | ||||
| 	args := kvstoreAPIReq{Method: "list", Params: kvstoreParams{Options: opts}} | ||||
| 	apiInput, err := json.Marshal(args) | ||||
| 	if err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
|  | ||||
| 	cmd := a.runOpts.Command("kvstore", "api") | ||||
| 	cmd.Stdin = strings.NewReader(string(apiInput)) | ||||
| 	bytes, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return result, APIError{err} | ||||
| 	} | ||||
|  | ||||
| 	var namespaces ListNamespacesRes | ||||
| 	err = json.Unmarshal(bytes, &namespaces) | ||||
| 	if err != nil { | ||||
| 		return result, UnmarshalError{err} | ||||
| 	} | ||||
| 	if namespaces.Error.Message != "" { | ||||
| 		return result, namespaces.Error | ||||
| 	} | ||||
| 	return namespaces.Result, nil | ||||
| } | ||||
|  | ||||
| func (a *API) ListEntryKeys(teamName *string, namespace string) (result keybase1.KVListEntryResult, err error) { | ||||
|  | ||||
| 	opts := kvstoreOptions{ | ||||
| 		Team:      teamName, | ||||
| 		Namespace: &namespace, | ||||
| 	} | ||||
| 	args := kvstoreAPIReq{Method: "list", Params: kvstoreParams{Options: opts}} | ||||
| 	apiInput, err := json.Marshal(args) | ||||
| 	if err != nil { | ||||
| 		return result, err | ||||
| 	} | ||||
|  | ||||
| 	cmd := a.runOpts.Command("kvstore", "api") | ||||
| 	cmd.Stdin = strings.NewReader(string(apiInput)) | ||||
| 	bytes, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return result, APIError{err} | ||||
| 	} | ||||
|  | ||||
| 	entryKeys := ListEntryKeysRes{} | ||||
| 	err = json.Unmarshal(bytes, &entryKeys) | ||||
| 	if err != nil { | ||||
| 		return result, UnmarshalError{err} | ||||
| 	} | ||||
| 	if entryKeys.Error.Message != "" { | ||||
| 		return result, entryKeys.Error | ||||
| 	} | ||||
| 	return entryKeys.Result, nil | ||||
| } | ||||
							
								
								
									
										85
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										85
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,25 +1,18 @@ | ||||
| package kbchat | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/keybase1" | ||||
| ) | ||||
|  | ||||
| type ListTeamMembers struct { | ||||
| 	Result ListTeamMembersResult `json:"result"` | ||||
| 	Error  Error             `json:"error"` | ||||
| } | ||||
|  | ||||
| type ListTeamMembersResult struct { | ||||
| 	Members ListTeamMembersResultMembers `json:"members"` | ||||
| } | ||||
|  | ||||
| type ListTeamMembersResultMembers struct { | ||||
| 	Owners  []ListMembersOutputMembersCategory `json:"owners"` | ||||
| 	Admins  []ListMembersOutputMembersCategory `json:"admins"` | ||||
| 	Writers []ListMembersOutputMembersCategory `json:"writers"` | ||||
| 	Readers []ListMembersOutputMembersCategory `json:"readers"` | ||||
| 	Result keybase1.TeamDetails `json:"result"` | ||||
| 	Error  Error                `json:"error"` | ||||
| } | ||||
|  | ||||
| type ListMembersOutputMembersCategory struct { | ||||
| @@ -28,62 +21,64 @@ type ListMembersOutputMembersCategory struct { | ||||
| } | ||||
|  | ||||
| type ListUserMemberships struct { | ||||
| 	Result ListUserMembershipsResult `json:"result"` | ||||
| 	Error  Error             `json:"error"` | ||||
| 	Result keybase1.AnnotatedTeamList `json:"result"` | ||||
| 	Error  Error                      `json:"error"` | ||||
| } | ||||
|  | ||||
| type ListUserMembershipsResult struct { | ||||
| 	Teams []ListUserMembershipsResultTeam `json:"teams"` | ||||
| } | ||||
|  | ||||
| type ListUserMembershipsResultTeam struct { | ||||
| 	TeamName string `json:"fq_name"` | ||||
| 	IsImplicitTeam bool `json:"is_implicit_team"` | ||||
| 	IsOpenTeam bool `json:"is_open_team"` | ||||
| 	Role int `json:"role"` | ||||
| 	MemberCount int `json:"member_count"` | ||||
| } | ||||
|  | ||||
| func (a *API) ListMembersOfTeam(teamName string) (ListTeamMembersResultMembers, error) { | ||||
| 	empty := ListTeamMembersResultMembers{} | ||||
|  | ||||
| 	apiInput := fmt.Sprintf(`{"method": "list-team-memberships", "params": {"options": {"team": "%s"}}}`, teamName) | ||||
| func (a *API) ListMembersOfTeam(teamName string) (res keybase1.TeamMembersDetails, err error) { | ||||
| 	teamNameEscaped, err := json.Marshal(teamName) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "list-team-memberships", "params": {"options": {"team": %s}}}`, teamNameEscaped) | ||||
| 	cmd := a.runOpts.Command("team", "api") | ||||
| 	cmd.Stdin = strings.NewReader(apiInput) | ||||
| 	bytes, err := cmd.CombinedOutput() | ||||
| 	var stderr bytes.Buffer | ||||
| 	cmd.Stderr = &stderr | ||||
| 	output, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to call keybase team api: %v", err) | ||||
| 		return res, APIError{err} | ||||
| 	} | ||||
| 	if stderr.Len() != 0 { | ||||
| 		log.Printf("ListMembersOfTeam error: %s", stderr.String()) | ||||
| 	} | ||||
|  | ||||
| 	members := ListTeamMembers{} | ||||
| 	err = json.Unmarshal(bytes, &members) | ||||
| 	err = json.Unmarshal(output, &members) | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to parse output from keybase team api: %v", err) | ||||
| 		return res, UnmarshalError{err} | ||||
| 	} | ||||
| 	if members.Error.Message != "" { | ||||
| 		return empty, fmt.Errorf("received error from keybase team api: %s", members.Error.Message) | ||||
| 		return res, members.Error | ||||
| 	} | ||||
| 	return members.Result.Members, nil | ||||
| } | ||||
|  | ||||
| func (a *API) ListUserMemberships(username string) ([]ListUserMembershipsResultTeam, error) { | ||||
| 	empty := []ListUserMembershipsResultTeam{} | ||||
|  | ||||
| 	apiInput := fmt.Sprintf(`{"method": "list-user-memberships", "params": {"options": {"username": "%s"}}}`, username) | ||||
| func (a *API) ListUserMemberships(username string) ([]keybase1.AnnotatedMemberInfo, error) { | ||||
| 	usernameEscaped, err := json.Marshal(username) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	apiInput := fmt.Sprintf(`{"method": "list-user-memberships", "params": {"options": {"username": %s}}}`, usernameEscaped) | ||||
| 	cmd := a.runOpts.Command("team", "api") | ||||
| 	cmd.Stdin = strings.NewReader(apiInput) | ||||
| 	bytes, err := cmd.CombinedOutput() | ||||
| 	var stderr bytes.Buffer | ||||
| 	cmd.Stderr = &stderr | ||||
| 	output, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to call keybase team api: %v", err) | ||||
| 		return nil, APIError{err} | ||||
| 	} | ||||
| 	if stderr.Len() != 0 { | ||||
| 		log.Printf("ListUserMemberships error: %s", stderr.String()) | ||||
| 	} | ||||
|  | ||||
| 	members := ListUserMemberships{} | ||||
| 	err = json.Unmarshal(bytes, &members) | ||||
| 	err = json.Unmarshal(output, &members) | ||||
| 	if err != nil { | ||||
| 		return empty, fmt.Errorf("failed to parse output from keybase team api: %v", err) | ||||
| 		return nil, UnmarshalError{err} | ||||
| 	} | ||||
| 	if members.Error.Message != "" { | ||||
| 		return empty, fmt.Errorf("received error from keybase team api: %s", members.Error.Message) | ||||
| 		return nil, members.Error | ||||
| 	} | ||||
| 	return members.Result.Teams, nil | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user