forked from lug/matterbridge
		
	Compare commits
	
		
			73 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 87788f354f | ||
|   | 7d2e440c83 | ||
|   | 5551f9d56f | ||
|   | 1fb91c6316 | ||
|   | e60949ff3f | ||
|   | 278a3c6890 | ||
|   | fcf734eb36 | ||
|   | cf3cddafab | ||
|   | c52664f22e | ||
|   | cb712ff37d | ||
|   | f4ae610448 | ||
|   | 601b8bc98d | ||
|   | 80b4cec87a | ||
|   | 76c7b69e4e | ||
|   | a5bd3c4dda | ||
|   | f06e9b5605 | ||
|   | 7a3bb0e55c | ||
|   | 6e8f535e8b | ||
|   | 5619a75b05 | ||
|   | 53dfb78215 | ||
|   | 8e97cbab1e | ||
|   | ce7b749fd5 | ||
|   | 6617bd6609 | ||
|   | e610fb3201 | ||
|   | 40f1d35415 | ||
|   | b79bf7d414 | ||
|   | 3724cc3a15 | ||
|   | 3418e8c9af | ||
|   | 9619dff334 | ||
|   | 1b2feb19e5 | ||
|   | 1829dc3d9f | ||
|   | bd0e81f5a0 | ||
|   | f04d360ee2 | ||
|   | 92f27281fa | ||
|   | 65781b9316 | ||
|   | 9be0be0316 | ||
|   | 9f5f004725 | ||
|   | fed77cccf3 | ||
|   | 9b520dfb78 | ||
|   | 8ad2be10b2 | ||
|   | 2d277a15f5 | ||
|   | d60468bb05 | ||
|   | 82d6210464 | ||
|   | ff198042d2 | ||
|   | 6b47e29583 | ||
|   | 380c38674c | ||
|   | 3c14a0891e | ||
|   | 8513a07416 | ||
|   | 220485a849 | ||
|   | 4db34b0506 | ||
|   | 5677c912a8 | ||
|   | 7a24de15e4 | ||
|   | 99d9ea283a | ||
|   | dac92a0e0a | ||
|   | a25efb16f3 | ||
|   | e4d73b29a1 | ||
|   | 8a875f292e | ||
|   | 60a85621ea | ||
|   | 115d20373c | ||
|   | cdf33e5748 | ||
|   | 01d0a9f412 | ||
|   | 8cc2d3b4fe | ||
|   | aba9e4f3be | ||
|   | 4d575ba13a | ||
|   | 7f0e4ad448 | ||
|   | 17cc14a9d2 | ||
|   | 1f8016182c | ||
|   | caf9ef2c4b | ||
|   | 64b57f2da3 | ||
|   | efd2c99862 | ||
|   | cc05ba8907 | ||
|   | 16763b715a | ||
|   | ffaa598796 | 
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | # Exclude matterbridge binary | ||||||
|  | matterbridge | ||||||
|  |  | ||||||
|  | # Exclude configuration file | ||||||
|  | matterbridge.toml | ||||||
| @@ -7,7 +7,7 @@ run: | |||||||
|   # concurrency: 4 |   # concurrency: 4 | ||||||
|  |  | ||||||
|   # timeout for analysis, e.g. 30s, 5m, default is 1m |   # timeout for analysis, e.g. 30s, 5m, default is 1m | ||||||
|   deadline: 1m |   deadline: 2m | ||||||
|  |  | ||||||
|   # exit code when at least one issue was found, default is 1 |   # exit code when at least one issue was found, default is 1 | ||||||
|   issues-exit-code: 1 |   issues-exit-code: 1 | ||||||
| @@ -105,10 +105,6 @@ linters-settings: | |||||||
|     # with golangci-lint call it on a directory with the changed file. |     # with golangci-lint call it on a directory with the changed file. | ||||||
|     check-exported: false |     check-exported: false | ||||||
|   unparam: |   unparam: | ||||||
|     # call graph construction algorithm (cha, rta). In general, use cha for libraries, |  | ||||||
|     # and rta for programs with main packages. Default is cha. |  | ||||||
|     algo: rta |  | ||||||
|  |  | ||||||
|     # Inspect exported functions, default is false. Set to true if no external program/library imports your code. |     # Inspect exported functions, default is false. Set to true if no external program/library imports your code. | ||||||
|     # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: |     # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: | ||||||
|     # if it's called for subdir of a project it can't find external interfaces. All text editor integrations |     # if it's called for subdir of a project it can't find external interfaces. All text editor integrations | ||||||
|   | |||||||
							
								
								
									
										89
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,75 +1,56 @@ | |||||||
| language: go | language: go | ||||||
| go: |  | ||||||
|     - 1.11.x |  | ||||||
| go_import_path: github.com/42wim/matterbridge | go_import_path: github.com/42wim/matterbridge | ||||||
|  |  | ||||||
| # we have everything vendored | # We have everything vendored so this helps TravisCI not run `go get ...`. | ||||||
| install: true | install: true | ||||||
|  |  | ||||||
| git: | git: | ||||||
|   depth: 200 |   depth: 200 | ||||||
|  |  | ||||||
| env: |  | ||||||
|   global: |  | ||||||
|     - GOOS=linux GOARCH=amd64 |  | ||||||
|     - GO111MODULE=on |  | ||||||
|     - GOLANGCI_VERSION="v1.14.0" |  | ||||||
|  |  | ||||||
| matrix: |  | ||||||
|   # It's ok if our code fails on unstable development versions of Go. |  | ||||||
|   allow_failures: |  | ||||||
|     - go: tip |  | ||||||
|   # Don't wait for tip tests to finish. Mark the test run green if the |  | ||||||
|   # tests pass on the stable versions of Go. |  | ||||||
|   fast_finish: true |  | ||||||
|  |  | ||||||
| notifications: | notifications: | ||||||
|       email: false |   email: false | ||||||
|  |  | ||||||
| before_script: |  | ||||||
|   # Get version info from tags. |  | ||||||
|   - MY_VERSION="$(git describe --tags)" |  | ||||||
|   # Retrieve the golangci-lint linter binary. |  | ||||||
|   - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION} |  | ||||||
|   # Retrieve and prepare CodeClimate's test coverage reporter. |  | ||||||
|   - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter |  | ||||||
|   - chmod +x ./cc-test-reporter |  | ||||||
|   - ./cc-test-reporter before-build |  | ||||||
|  |  | ||||||
| script: |  | ||||||
|   # Ensure that the module files are being kept correctly and that vendored dependencies are up-to-date. |  | ||||||
|   - go mod tidy |  | ||||||
|   - go mod vendor |  | ||||||
|   - git diff --exit-code --quiet || (echo "Please run 'go mod tidy' to clean up the 'go.mod' and 'go.sum' files."; false) |  | ||||||
|  |  | ||||||
|   # Run the linter. |  | ||||||
|   - golangci-lint run |  | ||||||
|  |  | ||||||
|   # Run all the tests with the race detector and generate coverage. |  | ||||||
|   - go test -v -race -coverprofile c.out ./... |  | ||||||
|  |  | ||||||
|   # Run the build script to generate the necessary binaries and images. |  | ||||||
|   - /bin/bash ci/bintray.sh |  | ||||||
|  |  | ||||||
| after_script: |  | ||||||
|   # Upload test coverage to CodeClimate. |  | ||||||
|   - ./cc-test-reporter after-build --exit-code ${TRAVIS_TEST_RESULT} |  | ||||||
|  |  | ||||||
| branches: | branches: | ||||||
|   only: |   only: | ||||||
|     - master |   - master | ||||||
|  |   - /.*/ | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   include: | ||||||
|  |   - stage: lint | ||||||
|  |     # Run linting in one Go environment only. | ||||||
|  |     script: ./ci/lint.sh | ||||||
|  |     go: 1.12.x | ||||||
|  |     env: | ||||||
|  |     - GO111MODULE=on | ||||||
|  |     - GOLANGCI_VERSION="v1.17.1" | ||||||
|  |   - stage: test | ||||||
|  |     # Run tests in a combination of Go environments. | ||||||
|  |     script: ./ci/test.sh | ||||||
|  |     go: 1.11.x | ||||||
|  |     env: | ||||||
|  |     - GO111MODULE=off | ||||||
|  |   - script: ./ci/test.sh | ||||||
|  |     go: 1.11.x | ||||||
|  |     env: | ||||||
|  |     - GO111MODULE=on | ||||||
|  |   - script: ./ci/test.sh | ||||||
|  |     go: 1.12.x | ||||||
|  |     env: | ||||||
|  |     - GO111MODULE=on | ||||||
|  |     - REPORT_COVERAGE=1 | ||||||
|  |     - BINDEPLOY=1 | ||||||
|  |  | ||||||
|  | before_deploy: /bin/bash ci/bintray.sh | ||||||
|  |  | ||||||
| deploy: | deploy: | ||||||
|   on: |  | ||||||
|      all_branches: true |  | ||||||
|   provider: bintray |  | ||||||
|   on: |   on: | ||||||
|     all_branches: true |     all_branches: true | ||||||
|  |     condition: $BINDEPLOY = 1 | ||||||
|  |   provider: bintray | ||||||
|   edge: |   edge: | ||||||
|     branch: v1.8.47 |     branch: v1.8.47 | ||||||
|   file: ci/deploy.json |   file: ci/deploy.json | ||||||
|   user: 42wim |   user: 42wim | ||||||
|   on: |  | ||||||
|      all_branches: true |  | ||||||
|   key: |   key: | ||||||
|      secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" |     secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								README.md
									
									
									
									
									
								
							| @@ -35,6 +35,12 @@ | |||||||
|  |  | ||||||
| **Note:** Matter<em>most</em> isn't required to run matter<em>bridge</em>.</sup></div> | **Note:** Matter<em>most</em> isn't required to run matter<em>bridge</em>.</sup></div> | ||||||
|  |  | ||||||
|  | <p> | ||||||
|  |   <a href="https://www.digitalocean.com/"> | ||||||
|  |     <img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_blue.svg" width="201px"> | ||||||
|  |   </a> | ||||||
|  | </p> | ||||||
|  |  | ||||||
| ### Table of Contents | ### Table of Contents | ||||||
|  * [Features](https://github.com/42wim/matterbridge/wiki/Features) |  * [Features](https://github.com/42wim/matterbridge/wiki/Features) | ||||||
|    * [Natively supported](#natively-supported) |    * [Natively supported](#natively-supported) | ||||||
| @@ -42,11 +48,12 @@ | |||||||
|    * [API](#API) |    * [API](#API) | ||||||
|  * [Chat with us](#chat-with-us) |  * [Chat with us](#chat-with-us) | ||||||
|  * [Screenshots](https://github.com/42wim/matterbridge/wiki/) |  * [Screenshots](https://github.com/42wim/matterbridge/wiki/) | ||||||
|  * [Installing](#installing) |  * [Installing/upgrading](#installing--upgrading) | ||||||
|    * [Binaries](#binaries) |    * [Binaries](#binaries) | ||||||
|    * [Building](#building) |  * [Building](#building) | ||||||
|  * [Configuration](#configuration) |  * [Configuration](#configuration) | ||||||
|    * [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) |    * [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) | ||||||
|  |    * [Settings](#settings) | ||||||
|    * [Examples](#examples) |    * [Examples](#examples) | ||||||
|  * [Running](#running) |  * [Running](#running) | ||||||
|    * [Docker](#docker) |    * [Docker](#docker) | ||||||
| @@ -75,7 +82,6 @@ | |||||||
| * [Slack](https://slack.com) | * [Slack](https://slack.com) | ||||||
| * [Discord](https://discordapp.com) | * [Discord](https://discordapp.com) | ||||||
| * [Telegram](https://telegram.org) | * [Telegram](https://telegram.org) | ||||||
| * [Hipchat](https://www.hipchat.com) |  | ||||||
| * [Rocket.chat](https://rocket.chat) | * [Rocket.chat](https://rocket.chat) | ||||||
| * [Matrix](https://matrix.org) | * [Matrix](https://matrix.org) | ||||||
| * [Steam](https://store.steampowered.com/) | * [Steam](https://store.steampowered.com/) | ||||||
| @@ -91,7 +97,7 @@ | |||||||
| * [Discourse](https://github.com/DeclanHoare/matterbabble) | * [Discourse](https://github.com/DeclanHoare/matterbabble) | ||||||
|  |  | ||||||
| ### API | ### API | ||||||
| The API is very basic at the moment.    | The API is basic at the moment. | ||||||
| More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/Api). | More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/Api). | ||||||
|  |  | ||||||
| Used by the projects below. Feel free to make a PR to add your project to this list. | Used by the projects below. Feel free to make a PR to add your project to this list. | ||||||
| @@ -113,7 +119,7 @@ Questions or want to test on your favorite platform? Join below: | |||||||
| * [Slack][mb-slack] | * [Slack][mb-slack] | ||||||
| * [Mattermost][mb-mattermost] | * [Mattermost][mb-mattermost] | ||||||
| * [Rocket.Chat][mb-rocketchat] | * [Rocket.Chat][mb-rocketchat] | ||||||
| * [XMPP][mb-xmpp] | * [XMPP][mb-xmpp] (matterbridge@conference.jabber.de) | ||||||
| * [Twitch][mb-twitch] | * [Twitch][mb-twitch] | ||||||
| * [Zulip][mb-zulip] | * [Zulip][mb-zulip] | ||||||
| * [Telegram][mb-telegram] | * [Telegram][mb-telegram] | ||||||
| @@ -121,15 +127,20 @@ Questions or want to test on your favorite platform? Join below: | |||||||
| ## Screenshots | ## Screenshots | ||||||
| See https://github.com/42wim/matterbridge/wiki | See https://github.com/42wim/matterbridge/wiki | ||||||
|  |  | ||||||
| ## Installing | ## Installing / upgrading | ||||||
| ### Binaries | ### Binaries | ||||||
| * Latest stable release [v1.14.1](https://github.com/42wim/matterbridge/releases/latest) | * Latest stable release [v1.15.1](https://github.com/42wim/matterbridge/releases/latest) | ||||||
| * Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/) | * 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. | ||||||
|  |  | ||||||
| ### Packages | ### Packages | ||||||
| * [Overview](https://repology.org/metapackage/matterbridge/versions) | * [Overview](https://repology.org/metapackage/matterbridge/versions) | ||||||
|  |  | ||||||
| ### Building | ## Building | ||||||
|  | Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest) | ||||||
|  |  | ||||||
|  | If you really want to build from source, follow these instructions: | ||||||
| Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH). | Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH). | ||||||
|  |  | ||||||
| After Go is setup, download matterbridge to your $GOPATH directory. | After Go is setup, download matterbridge to your $GOPATH directory. | ||||||
| @@ -150,6 +161,9 @@ matterbridge | |||||||
| ### Basic configuration | ### Basic configuration | ||||||
| See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. | See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. | ||||||
|  |  | ||||||
|  | ### Settings | ||||||
|  | All possible [settings](https://github.com/42wim/matterbridge/wiki/Settings) for each bridge. | ||||||
|  |  | ||||||
| ### Advanced configuration | ### Advanced configuration | ||||||
| * [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example. | * [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example. | ||||||
|  |  | ||||||
| @@ -264,7 +278,13 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | |||||||
| * https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/ | * https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/ | ||||||
|  |  | ||||||
| ## Thanks | ## Thanks | ||||||
| [](https://www.digitalocean.com/) for sponsoring demo/testing droplets. |  | ||||||
|  | <p>This project is supported by:</p> | ||||||
|  | <p> | ||||||
|  |   <a href="https://www.digitalocean.com/"> | ||||||
|  |     <img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px"> | ||||||
|  |   </a> | ||||||
|  | </p> | ||||||
|  |  | ||||||
| Matterbridge wouldn't exist without these libraries: | Matterbridge wouldn't exist without these libraries: | ||||||
| * discord - https://github.com/bwmarrin/discordgo | * discord - https://github.com/bwmarrin/discordgo | ||||||
|   | |||||||
| @@ -120,13 +120,14 @@ type Protocol struct { | |||||||
| 	ReplaceMessages        [][]string // all protocols | 	ReplaceMessages        [][]string // all protocols | ||||||
| 	ReplaceNicks           [][]string // all protocols | 	ReplaceNicks           [][]string // all protocols | ||||||
| 	RemoteNickFormat       string     // all protocols | 	RemoteNickFormat       string     // all protocols | ||||||
| 	RunCommands            []string   // irc | 	RunCommands            []string   // IRC | ||||||
| 	Server                 string     // IRC,mattermost,XMPP,discord | 	Server                 string     // IRC,mattermost,XMPP,discord | ||||||
| 	ShowJoinPart           bool       // all protocols | 	ShowJoinPart           bool       // all protocols | ||||||
| 	ShowTopicChange        bool       // slack | 	ShowTopicChange        bool       // slack | ||||||
| 	ShowUserTyping         bool       // slack | 	ShowUserTyping         bool       // slack | ||||||
| 	ShowEmbeds             bool       // discord | 	ShowEmbeds             bool       // discord | ||||||
| 	SkipTLSVerify          bool       // IRC, mattermost | 	SkipTLSVerify          bool       // IRC, mattermost | ||||||
|  | 	SkipVersionCheck       bool       // mattermost | ||||||
| 	StripNick              bool       // all protocols | 	StripNick              bool       // all protocols | ||||||
| 	SyncTopic              bool       // slack | 	SyncTopic              bool       // slack | ||||||
| 	TengoModifyMessage     string     // general | 	TengoModifyMessage     string     // general | ||||||
| @@ -141,6 +142,7 @@ type Protocol struct { | |||||||
| 	UseFirstName           bool       // telegram | 	UseFirstName           bool       // telegram | ||||||
| 	UseUserName            bool       // discord | 	UseUserName            bool       // discord | ||||||
| 	UseInsecureURL         bool       // telegram | 	UseInsecureURL         bool       // telegram | ||||||
|  | 	VerboseJoinPart        bool       // IRC | ||||||
| 	WebhookBindAddress     string     // mattermost, slack | 	WebhookBindAddress     string     // mattermost, slack | ||||||
| 	WebhookURL             string     // mattermost, slack | 	WebhookURL             string     // mattermost, slack | ||||||
| } | } | ||||||
| @@ -166,6 +168,13 @@ type Gateway struct { | |||||||
| 	InOut  []Bridge | 	InOut  []Bridge | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type Tengo struct { | ||||||
|  | 	InMessage        string | ||||||
|  | 	Message          string | ||||||
|  | 	RemoteNickFormat string | ||||||
|  | 	OutMessage       string | ||||||
|  | } | ||||||
|  |  | ||||||
| type SameChannelGateway struct { | type SameChannelGateway struct { | ||||||
| 	Name     string | 	Name     string | ||||||
| 	Enable   bool | 	Enable   bool | ||||||
| @@ -190,6 +199,7 @@ type BridgeValues struct { | |||||||
| 	WhatsApp           map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results | 	WhatsApp           map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results | ||||||
| 	Zulip              map[string]Protocol | 	Zulip              map[string]Protocol | ||||||
| 	General            Protocol | 	General            Protocol | ||||||
|  | 	Tengo              Tengo | ||||||
| 	Gateway            []Gateway | 	Gateway            []Gateway | ||||||
| 	SameChannelGateway []SameChannelGateway | 	SameChannelGateway []SameChannelGateway | ||||||
| } | } | ||||||
| @@ -245,12 +255,12 @@ func newConfigFromString(logger *logrus.Entry, input []byte) *config { | |||||||
| 	viper.AutomaticEnv() | 	viper.AutomaticEnv() | ||||||
|  |  | ||||||
| 	if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil { | 	if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil { | ||||||
| 		logger.Fatalf("Failed to parse the configuration: %#v", err) | 		logger.Fatalf("Failed to parse the configuration: %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cfg := &BridgeValues{} | 	cfg := &BridgeValues{} | ||||||
| 	if err := viper.Unmarshal(cfg); err != nil { | 	if err := viper.Unmarshal(cfg); err != nil { | ||||||
| 		logger.Fatalf("Failed to load the configuration: %#v", err) | 		logger.Fatalf("Failed to load the configuration: %s", err) | ||||||
| 	} | 	} | ||||||
| 	return &config{ | 	return &config{ | ||||||
| 		logger: logger, | 		logger: logger, | ||||||
|   | |||||||
| @@ -75,6 +75,7 @@ func (b *Bdiscord) Connect() error { | |||||||
| 	b.c.AddHandler(b.memberUpdate) | 	b.c.AddHandler(b.memberUpdate) | ||||||
| 	b.c.AddHandler(b.messageUpdate) | 	b.c.AddHandler(b.messageUpdate) | ||||||
| 	b.c.AddHandler(b.messageDelete) | 	b.c.AddHandler(b.messageDelete) | ||||||
|  | 	b.c.AddHandler(b.messageDeleteBulk) | ||||||
| 	b.c.AddHandler(b.memberAdd) | 	b.c.AddHandler(b.memberAdd) | ||||||
| 	b.c.AddHandler(b.memberRemove) | 	b.c.AddHandler(b.memberRemove) | ||||||
| 	err = b.c.Open() | 	err = b.c.Open() | ||||||
| @@ -95,11 +96,11 @@ func (b *Bdiscord) Connect() error { | |||||||
| 	for _, guild := range guilds { | 	for _, guild := range guilds { | ||||||
| 		if guild.Name == serverName || guild.ID == serverName { | 		if guild.Name == serverName || guild.ID == serverName { | ||||||
| 			b.channels, err = b.c.GuildChannels(guild.ID) | 			b.channels, err = b.c.GuildChannels(guild.ID) | ||||||
| 			b.guildID = guild.ID |  | ||||||
| 			guildFound = true |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
|  | 			b.guildID = guild.ID | ||||||
|  | 			guildFound = true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	b.channelsMutex.Unlock() | 	b.channelsMutex.Unlock() | ||||||
| @@ -208,11 +209,21 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 	b.channelsMutex.RUnlock() | 	b.channelsMutex.RUnlock() | ||||||
|  |  | ||||||
| 	// Use webhook to send the message | 	// Use webhook to send the message | ||||||
| 	if wID != "" { | 	if wID != "" && msg.Event != config.EventMsgDelete { | ||||||
| 		// skip events | 		// skip events | ||||||
| 		if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { | 		if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { | ||||||
| 			return "", nil | 			return "", nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// If we are editing a message, delete the old message | ||||||
|  | 		if msg.ID != "" { | ||||||
|  | 			b.Log.Debugf("Deleting edited webhook message") | ||||||
|  | 			err := b.c.ChannelMessageDelete(channelID, msg.ID) | ||||||
|  | 			if err != nil { | ||||||
|  | 				b.Log.Errorf("Could not delete edited webhook message: %s", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		b.Log.Debugf("Broadcasting using Webhook") | 		b.Log.Debugf("Broadcasting using Webhook") | ||||||
| 		for _, f := range msg.Extra["file"] { | 		for _, f := range msg.Extra["file"] { | ||||||
| 			fi := f.(config.FileInfo) | 			fi := f.(config.FileInfo) | ||||||
| @@ -250,7 +261,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 				return "", err | 				return "", err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		err := b.c.WebhookExecute( | 		msg, err := b.webhookExecute( | ||||||
| 			wID, | 			wID, | ||||||
| 			wToken, | 			wToken, | ||||||
| 			true, | 			true, | ||||||
| @@ -259,7 +270,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 				Username:  msg.Username, | 				Username:  msg.Username, | ||||||
| 				AvatarURL: msg.Avatar, | 				AvatarURL: msg.Avatar, | ||||||
| 			}) | 			}) | ||||||
| 		return "", err | 		return msg.ID, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.Log.Debugf("Broadcasting using token (API)") | 	b.Log.Debugf("Broadcasting using token (API)") | ||||||
|   | |||||||
| @@ -16,6 +16,27 @@ func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelet | |||||||
| 	b.Remote <- rmsg | 	b.Remote <- rmsg | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // TODO(qaisjp): if other bridges support bulk deletions, it could be fanned out centrally | ||||||
|  | func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageDeleteBulk) { //nolint:unparam | ||||||
|  | 	for _, msgID := range m.Messages { | ||||||
|  | 		rmsg := config.Message{ | ||||||
|  | 			Account: b.Account, | ||||||
|  | 			ID:      msgID, | ||||||
|  | 			Event:   config.EventMsgDelete, | ||||||
|  | 			Text:    config.EventMsgDelete, | ||||||
|  | 			Channel: "ID:" + m.ChannelID, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !b.useChannelID { | ||||||
|  | 			rmsg.Channel = b.getChannelName(m.ChannelID) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		b.Log.Debugf("<= Sending message from %s to gateway", b.Account) | ||||||
|  | 		b.Log.Debugf("<= Message is %#v", rmsg) | ||||||
|  | 		b.Remote <- rmsg | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam | func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam | ||||||
| 	if b.GetBool("EditDisable") { | 	if b.GetBool("EditDisable") { | ||||||
| 		return | 		return | ||||||
| @@ -36,7 +57,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	// if using webhooks, do not relay if it's ours | 	// if using webhooks, do not relay if it's ours | ||||||
| 	if b.useWebhook() && m.Author.Bot { // && b.isWebhookID(m.Author.ID) { | 	if b.useWebhook() && m.Author.Bot && b.isWebhookID(m.Author.ID) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package bdiscord | package bdiscord | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -51,6 +52,9 @@ func (b *Bdiscord) getGuildMemberByNick(nick string) (*discordgo.Member, error) | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bdiscord) getChannelID(name string) string { | func (b *Bdiscord) getChannelID(name string) string { | ||||||
|  | 	if strings.Contains(name, "/") { | ||||||
|  | 		return b.getCategoryChannelID(name) | ||||||
|  | 	} | ||||||
| 	b.channelsMutex.RLock() | 	b.channelsMutex.RLock() | ||||||
| 	defer b.channelsMutex.RUnlock() | 	defer b.channelsMutex.RUnlock() | ||||||
|  |  | ||||||
| @@ -59,25 +63,70 @@ func (b *Bdiscord) getChannelID(name string) string { | |||||||
| 		return idcheck[1] | 		return idcheck[1] | ||||||
| 	} | 	} | ||||||
| 	for _, channel := range b.channels { | 	for _, channel := range b.channels { | ||||||
| 		if channel.Name == name { | 		if channel.Name == name && channel.Type == discordgo.ChannelTypeGuildText { | ||||||
| 			return channel.ID | 			return channel.ID | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Bdiscord) getCategoryChannelID(name string) string { | ||||||
|  | 	b.channelsMutex.RLock() | ||||||
|  | 	defer b.channelsMutex.RUnlock() | ||||||
|  | 	res := strings.Split(name, "/") | ||||||
|  | 	// shouldn't happen because function should be only called from getChannelID | ||||||
|  | 	if len(res) != 2 { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	catName, chanName := res[0], res[1] | ||||||
|  | 	for _, channel := range b.channels { | ||||||
|  | 		// if we have a parentID, lookup the name of that parent (category) | ||||||
|  | 		// and if it matches return it | ||||||
|  | 		if channel.Name == chanName && channel.ParentID != "" { | ||||||
|  | 			for _, cat := range b.channels { | ||||||
|  | 				if cat.ID == channel.ParentID && cat.Name == catName { | ||||||
|  | 					return channel.ID | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
| func (b *Bdiscord) getChannelName(id string) string { | func (b *Bdiscord) getChannelName(id string) string { | ||||||
| 	b.channelsMutex.RLock() | 	b.channelsMutex.RLock() | ||||||
| 	defer b.channelsMutex.RUnlock() | 	defer b.channelsMutex.RUnlock() | ||||||
|  |  | ||||||
| 	for _, channel := range b.channels { | 	for _, channel := range b.channels { | ||||||
| 		if channel.ID == id { | 		if channel.ID == id { | ||||||
| 			return channel.Name | 			return b.getCategoryChannelName(channel.Name, channel.ParentID) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Bdiscord) getCategoryChannelName(name, parentID string) string { | ||||||
|  | 	var usesCat bool | ||||||
|  | 	// do we have a category configuration in the channel config | ||||||
|  | 	for _, c := range b.channelInfoMap { | ||||||
|  | 		if strings.Contains(c.Name, "/") { | ||||||
|  | 			usesCat = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// configuration without category, return the normal channel name | ||||||
|  | 	if !usesCat { | ||||||
|  | 		return name | ||||||
|  | 	} | ||||||
|  | 	// create a category/channel response | ||||||
|  | 	for _, c := range b.channels { | ||||||
|  | 		if c.ID == parentID { | ||||||
|  | 			name = c.Name + "/" + name | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return name | ||||||
|  | } | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// See https://discordapp.com/developers/docs/reference#message-formatting. | 	// See https://discordapp.com/developers/docs/reference#message-formatting. | ||||||
| 	channelMentionRE = regexp.MustCompile("<#[0-9]+>") | 	channelMentionRE = regexp.MustCompile("<#[0-9]+>") | ||||||
| @@ -87,12 +136,12 @@ var ( | |||||||
|  |  | ||||||
| func (b *Bdiscord) replaceChannelMentions(text string) string { | func (b *Bdiscord) replaceChannelMentions(text string) string { | ||||||
| 	replaceChannelMentionFunc := func(match string) string { | 	replaceChannelMentionFunc := func(match string) string { | ||||||
| 		var err error |  | ||||||
| 		channelID := match[2 : len(match)-1] | 		channelID := match[2 : len(match)-1] | ||||||
|  |  | ||||||
| 		channelName := b.getChannelName(channelID) | 		channelName := b.getChannelName(channelID) | ||||||
|  |  | ||||||
| 		// If we don't have the channel refresh our list. | 		// If we don't have the channel refresh our list. | ||||||
| 		if channelName == "" { | 		if channelName == "" { | ||||||
|  | 			var err error | ||||||
| 			b.channels, err = b.c.GuildChannels(b.guildID) | 			b.channels, err = b.c.GuildChannels(b.guildID) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return "#unknownchannel" | 				return "#unknownchannel" | ||||||
| @@ -187,3 +236,26 @@ func enumerateUsernames(s string) []string { | |||||||
| 	} | 	} | ||||||
| 	return usernames | 	return usernames | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // webhookExecute executes a webhook. | ||||||
|  | // webhookID: The ID of a webhook. | ||||||
|  | // token    : The auth token for the webhook | ||||||
|  | // wait	    : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) | ||||||
|  | func (b *Bdiscord) webhookExecute(webhookID, token string, wait bool, data *discordgo.WebhookParams) (st *discordgo.Message, err error) { | ||||||
|  | 	uri := discordgo.EndpointWebhookToken(webhookID, token) | ||||||
|  |  | ||||||
|  | 	if wait { | ||||||
|  | 		uri += "?wait=true" | ||||||
|  | 	} | ||||||
|  | 	response, err := b.c.RequestWithBucketID("POST", uri, data, discordgo.EndpointWebhookToken("", "")) | ||||||
|  | 	if !wait || err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = json.Unmarshal(response, &st) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, discordgo.ErrJSONUnmarshal | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return st, nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import ( | |||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"regexp" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -91,12 +90,13 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) { | |||||||
| 		if b.GetBool("nosendjoinpart") { | 		if b.GetBool("nosendjoinpart") { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account) |  | ||||||
| 		// QUIT isn't channel bound, happens for all channels on the bridge |  | ||||||
| 		if event.Command == "QUIT" { |  | ||||||
| 			channel = "" |  | ||||||
| 		} |  | ||||||
| 		msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave} | 		msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave} | ||||||
|  | 		if b.GetBool("verbosejoinpart") { | ||||||
|  | 			b.Log.Debugf("<= Sending verbose JOIN_LEAVE event from %s to gateway", b.Account) | ||||||
|  | 			msg = config.Message{Username: "system", Text: event.Source.Name + " (" + event.Source.Ident + "@" + event.Source.Host + ") " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave} | ||||||
|  | 		} else { | ||||||
|  | 			b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account) | ||||||
|  | 		} | ||||||
| 		b.Log.Debugf("<= Message is %#v", msg) | 		b.Log.Debugf("<= Message is %#v", msg) | ||||||
| 		b.Remote <- msg | 		b.Remote <- msg | ||||||
| 		return | 		return | ||||||
| @@ -160,7 +160,10 @@ func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) { | |||||||
| 	b.handleNickServ() | 	b.handleNickServ() | ||||||
| 	b.handleRunCommands() | 	b.handleRunCommands() | ||||||
| 	// we are now fully connected | 	// we are now fully connected | ||||||
| 	b.connected <- nil | 	// only send on first connection | ||||||
|  | 	if b.FirstConnection { | ||||||
|  | 		b.connected <- nil | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | ||||||
| @@ -178,10 +181,6 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | |||||||
| 	// strip action, we made an event if it was an action | 	// strip action, we made an event if it was an action | ||||||
| 	rmsg.Text += event.StripAction() | 	rmsg.Text += event.StripAction() | ||||||
|  |  | ||||||
| 	// strip IRC colors |  | ||||||
| 	re := regexp.MustCompile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`) |  | ||||||
| 	rmsg.Text = re.ReplaceAllString(rmsg.Text, "") |  | ||||||
|  |  | ||||||
| 	// start detecting the charset | 	// start detecting the charset | ||||||
| 	mycharset := b.GetString("Charset") | 	mycharset := b.GetString("Charset") | ||||||
| 	if mycharset == "" { | 	if mycharset == "" { | ||||||
|   | |||||||
| @@ -137,6 +137,7 @@ func (b *Birc) Send(msg config.Message) (string, error) { | |||||||
| 	// we can be in between reconnects #385 | 	// we can be in between reconnects #385 | ||||||
| 	if !b.i.IsConnected() { | 	if !b.i.IsConnected() { | ||||||
| 		b.Log.Error("Not connected to server, dropping message") | 		b.Log.Error("Not connected to server, dropping message") | ||||||
|  | 		return "", nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Execute a command | 	// Execute a command | ||||||
| @@ -231,7 +232,7 @@ func (b *Birc) getClient() (*girc.Client, error) { | |||||||
| 	// fix strict user handling of girc | 	// fix strict user handling of girc | ||||||
| 	user := b.GetString("Nick") | 	user := b.GetString("Nick") | ||||||
| 	for !girc.IsValidUser(user) { | 	for !girc.IsValidUser(user) { | ||||||
| 		if len(user) == 1 { | 		if len(user) == 1 || len(user) == 0 { | ||||||
| 			user = "matterbridge" | 			user = "matterbridge" | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -70,6 +70,7 @@ func (b *Bmattermost) apiLogin() error { | |||||||
| 		b.mc.SetLogLevel("debug") | 		b.mc.SetLogLevel("debug") | ||||||
| 	} | 	} | ||||||
| 	b.mc.SkipTLSVerify = b.GetBool("SkipTLSVerify") | 	b.mc.SkipTLSVerify = b.GetBool("SkipTLSVerify") | ||||||
|  | 	b.mc.SkipVersionCheck = b.GetBool("SkipVersionCheck") | ||||||
| 	b.mc.NoTLS = b.GetBool("NoTLS") | 	b.mc.NoTLS = b.GetBool("NoTLS") | ||||||
| 	b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server")) | 	b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server")) | ||||||
| 	err := b.mc.Login() | 	err := b.mc.Login() | ||||||
| @@ -186,6 +187,12 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool { | |||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Ignore non-post messages | ||||||
|  | 	if message.Post == nil { | ||||||
|  | 		b.Log.Debugf("ignoring nil message.Post: %#v", message) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Ignore messages sent from matterbridge | 	// Ignore messages sent from matterbridge | ||||||
| 	if message.Post.Props != nil { | 	if message.Post.Props != nil { | ||||||
| 		if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok { | 		if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok { | ||||||
|   | |||||||
| @@ -121,6 +121,12 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) { | |||||||
| 		return msg.ID, b.mc.DeleteMessage(msg.ID) | 		return msg.ID, b.mc.DeleteMessage(msg.ID) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Handle prefix hint for unthreaded messages. | ||||||
|  | 	if msg.ParentID == "msg-parent-not-found" { | ||||||
|  | 		msg.ParentID = "" | ||||||
|  | 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Upload a file if it exists | 	// Upload a file if it exists | ||||||
| 	if msg.Extra != nil { | 	if msg.Extra != nil { | ||||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||||
|   | |||||||
| @@ -95,7 +95,7 @@ func (b *Brocketchat) getChannelID(name string) string { | |||||||
| 	b.RLock() | 	b.RLock() | ||||||
| 	defer b.RUnlock() | 	defer b.RUnlock() | ||||||
| 	for k, v := range b.channelMap { | 	for k, v := range b.channelMap { | ||||||
| 		if v == name { | 		if v == name || v == "#"+name { | ||||||
| 			return k | 			return k | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -108,6 +108,11 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) { | |||||||
| 	msg.Channel = strings.TrimPrefix(msg.Channel, "#") | 	msg.Channel = strings.TrimPrefix(msg.Channel, "#") | ||||||
| 	channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel} | 	channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel} | ||||||
|  |  | ||||||
|  | 	// Make a action /me of the message | ||||||
|  | 	if msg.Event == config.EventUserAction { | ||||||
|  | 		msg.Text = "_" + msg.Text + "_" | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Delete message | 	// Delete message | ||||||
| 	if msg.Event == config.EventMsgDelete { | 	if msg.Event == config.EventMsgDelete { | ||||||
| 		if msg.ID == "" { | 		if msg.ID == "" { | ||||||
|   | |||||||
| @@ -22,20 +22,20 @@ func (b *Bslack) handleSlack() { | |||||||
| 	time.Sleep(time.Second) | 	time.Sleep(time.Second) | ||||||
| 	b.Log.Debug("Start listening for Slack messages") | 	b.Log.Debug("Start listening for Slack messages") | ||||||
| 	for message := range messages { | 	for message := range messages { | ||||||
| 		if message.Event != config.EventUserTyping { | 		// don't do any action on deleted/typing messages | ||||||
|  | 		if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete { | ||||||
| 			b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account) | 			b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account) | ||||||
|  | 			// cleanup the message | ||||||
|  | 			message.Text = b.replaceMention(message.Text) | ||||||
|  | 			message.Text = b.replaceVariable(message.Text) | ||||||
|  | 			message.Text = b.replaceChannel(message.Text) | ||||||
|  | 			message.Text = b.replaceURL(message.Text) | ||||||
|  | 			message.Text = html.UnescapeString(message.Text) | ||||||
|  |  | ||||||
|  | 			// Add the avatar | ||||||
|  | 			message.Avatar = b.users.getAvatar(message.UserID) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// cleanup the message |  | ||||||
| 		message.Text = b.replaceMention(message.Text) |  | ||||||
| 		message.Text = b.replaceVariable(message.Text) |  | ||||||
| 		message.Text = b.replaceChannel(message.Text) |  | ||||||
| 		message.Text = b.replaceURL(message.Text) |  | ||||||
| 		message.Text = html.UnescapeString(message.Text) |  | ||||||
|  |  | ||||||
| 		// Add the avatar |  | ||||||
| 		message.Avatar = b.users.getAvatar(message.UserID) |  | ||||||
|  |  | ||||||
| 		b.Log.Debugf("<= Message is %#v", message) | 		b.Log.Debugf("<= Message is %#v", message) | ||||||
| 		b.Remote <- *message | 		b.Remote <- *message | ||||||
| 	} | 	} | ||||||
| @@ -130,12 +130,18 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool { | |||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// It seems ev.SubMessage.Edited == nil when slack unfurls. | 	if ev.SubMessage != nil { | ||||||
| 	// Do not forward these messages. See Github issue #266. | 		// It seems ev.SubMessage.Edited == nil when slack unfurls. | ||||||
| 	if ev.SubMessage != nil && | 		// Do not forward these messages. See Github issue #266. | ||||||
| 		ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp && | 		if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp && | ||||||
| 		ev.SubMessage.Edited == nil { | 			ev.SubMessage.Edited == nil { | ||||||
| 		return true | 			return true | ||||||
|  | 		} | ||||||
|  | 		// see hidden subtypes at https://api.slack.com/events/message | ||||||
|  | 		// these messages are sent when we add a message to a thread #709 | ||||||
|  | 		if ev.SubType == "message_replied" && ev.Hidden { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(ev.Files) > 0 { | 	if len(ev.Files) > 0 { | ||||||
|   | |||||||
| @@ -13,7 +13,9 @@ type BLegacy struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func NewLegacy(cfg *bridge.Config) bridge.Bridger { | func NewLegacy(cfg *bridge.Config) bridge.Bridger { | ||||||
| 	return &BLegacy{Bslack: newBridge(cfg)} | 	b := &BLegacy{Bslack: newBridge(cfg)} | ||||||
|  | 	b.legacy = true | ||||||
|  | 	return b | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *BLegacy) Connect() error { | func (b *BLegacy) Connect() error { | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ type Bslack struct { | |||||||
|  |  | ||||||
| 	channels *channels | 	channels *channels | ||||||
| 	users    *users | 	users    *users | ||||||
|  | 	legacy   bool | ||||||
| } | } | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -151,6 +152,18 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// try to join a channel when in legacy | ||||||
|  | 	if b.legacy { | ||||||
|  | 		_, err := b.sc.JoinChannel(channel.Name) | ||||||
|  | 		if err != nil { | ||||||
|  | 			switch err.Error() { | ||||||
|  | 			case "name_taken", "restricted_action": | ||||||
|  | 			case "default": | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	b.channels.populateChannels(false) | 	b.channels.populateChannels(false) | ||||||
|  |  | ||||||
| 	channelInfo, err := b.channels.getChannel(channel.Name) | 	channelInfo, err := b.channels.getChannel(channel.Name) | ||||||
| @@ -163,7 +176,8 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error { | |||||||
| 		channel.Name = channelInfo.Name | 		channel.Name = channelInfo.Name | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !channelInfo.IsMember { | 	// we can't join a channel unless we are using legacy tokens #651 | ||||||
|  | 	if !channelInfo.IsMember && !b.legacy { | ||||||
| 		return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name) | 		return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
|   | |||||||
| @@ -87,6 +87,11 @@ func (b *users) populateUser(userID string) { | |||||||
| 			// in case the previous query failed for some reason. | 			// in case the previous query failed for some reason. | ||||||
| 		} else { | 		} else { | ||||||
| 			b.usersSyncPoints[userID] = make(chan struct{}) | 			b.usersSyncPoints[userID] = make(chan struct{}) | ||||||
|  | 			defer func() { | ||||||
|  | 				// Wake up any waiting goroutines and remove the synchronization point. | ||||||
|  | 				close(b.usersSyncPoints[userID]) | ||||||
|  | 				delete(b.usersSyncPoints, userID) | ||||||
|  | 			}() | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -106,10 +111,6 @@ func (b *users) populateUser(userID string) { | |||||||
|  |  | ||||||
| 	// Register user information. | 	// Register user information. | ||||||
| 	b.users[userID] = user | 	b.users[userID] = user | ||||||
|  |  | ||||||
| 	// Wake up any waiting goroutines and remove the synchronization point. |  | ||||||
| 	close(b.usersSyncPoints[userID]) |  | ||||||
| 	delete(b.usersSyncPoints, userID) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *users) populateUsers(wait bool) { | func (b *users) populateUsers(wait bool) { | ||||||
|   | |||||||
| @@ -5,10 +5,11 @@ import ( | |||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"unicode/utf16" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
| 	"github.com/42wim/matterbridge/bridge/helper" | 	"github.com/42wim/matterbridge/bridge/helper" | ||||||
| 	"github.com/go-telegram-bot-api/telegram-bot-api" | 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *tgbotapi.Message) *tgbotapi.Message { | func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *tgbotapi.Message) *tgbotapi.Message { | ||||||
| @@ -125,6 +126,11 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | |||||||
| 		// handle groups | 		// handle groups | ||||||
| 		message = b.handleGroups(&rmsg, message, update) | 		message = b.handleGroups(&rmsg, message, update) | ||||||
|  |  | ||||||
|  | 		if message == nil { | ||||||
|  | 			b.Log.Error("message is nil, this shouldn't happen.") | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// set the ID's from the channel or group message | 		// set the ID's from the channel or group message | ||||||
| 		rmsg.ID = strconv.Itoa(message.MessageID) | 		rmsg.ID = strconv.Itoa(message.MessageID) | ||||||
| 		rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10) | 		rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10) | ||||||
| @@ -370,8 +376,13 @@ func (b *Btelegram) handleEntities(rmsg *config.Message, message *tgbotapi.Messa | |||||||
| 				b.Log.Errorf("entity text_link url parse failed: %s", err) | 				b.Log.Errorf("entity text_link url parse failed: %s", err) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			link := rmsg.Text[e.Offset : e.Offset+e.Length] | 			utfEncodedString := utf16.Encode([]rune(rmsg.Text)) | ||||||
| 			rmsg.Text = strings.Replace(rmsg.Text, link, url.String(), 1) | 			if e.Offset+e.Length > len(utfEncodedString) { | ||||||
|  | 				b.Log.Errorf("entity length is too long %d > %d", e.Offset+e.Length, len(utfEncodedString)) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			link := utf16.Decode(utfEncodedString[e.Offset : e.Offset+e.Length]) | ||||||
|  | 			rmsg.Text = strings.Replace(rmsg.Text, string(link), url.String(), 1) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,10 +5,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp" | ||||||
| 	"github.com/matterbridge/go-whatsapp" |  | ||||||
|  |  | ||||||
| 	whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -21,6 +18,10 @@ Check: | |||||||
|  |  | ||||||
| // HandleError received from WhatsApp | // HandleError received from WhatsApp | ||||||
| func (b *Bwhatsapp) HandleError(err error) { | 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") { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types | 	b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -57,7 +58,7 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | |||||||
|  |  | ||||||
| 			// mentions comes as telephone numbers and we don't want to expose it to other bridges | 			// mentions comes as telephone numbers and we don't want to expose it to other bridges | ||||||
| 			// replace it with something more meaninful to others | 			// replace it with something more meaninful to others | ||||||
| 			mention := b.getSenderNotify(numberAndSuffix[0] + whatsappExt.NewUserSuffix) | 			mention := b.getSenderNotify(numberAndSuffix[0] + "@s.whatsapp.net") | ||||||
| 			if mention == "" { | 			if mention == "" { | ||||||
| 				mention = "someone" | 				mention = "someone" | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -2,13 +2,22 @@ package bwhatsapp | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/gob" | 	"encoding/gob" | ||||||
|  | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go" | 	qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go" | ||||||
| 	"github.com/matterbridge/go-whatsapp" | 	"github.com/Rhymen/go-whatsapp" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type ProfilePicInfo struct { | ||||||
|  | 	URL string `json:"eurl"` | ||||||
|  | 	Tag string `json:"tag"` | ||||||
|  |  | ||||||
|  | 	Status int16 `json:"status"` | ||||||
|  | } | ||||||
|  |  | ||||||
| func qrFromTerminal(invert bool) chan string { | func qrFromTerminal(invert bool) chan string { | ||||||
| 	qr := make(chan string) | 	qr := make(chan string) | ||||||
| 	go func() { | 	go func() { | ||||||
| @@ -82,3 +91,17 @@ func (b *Bwhatsapp) getSenderNotify(senderJid string) string { | |||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) { | ||||||
|  | 	data, err := b.conn.GetProfilePicThumb(jid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to get avatar: %v", err) | ||||||
|  | 	} | ||||||
|  | 	content := <-data | ||||||
|  | 	info := &ProfilePicInfo{} | ||||||
|  | 	err = json.Unmarshal([]byte(content), info) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return info, fmt.Errorf("failed to unmarshal avatar info: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return info, nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -11,10 +11,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp" | ||||||
| 	"github.com/matterbridge/go-whatsapp" |  | ||||||
|  |  | ||||||
| 	whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -29,10 +26,8 @@ type Bwhatsapp struct { | |||||||
| 	*bridge.Config | 	*bridge.Config | ||||||
|  |  | ||||||
| 	// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21 | 	// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21 | ||||||
| 	session *whatsapp.Session | 	session   *whatsapp.Session | ||||||
| 	conn    *whatsapp.Conn | 	conn      *whatsapp.Conn | ||||||
| 	// https://github.com/tulir/mautrix-whatsapp/blob/master/whatsapp-ext/whatsapp.go |  | ||||||
| 	connExt   *whatsappExt.ExtendedConn |  | ||||||
| 	startedAt uint64 | 	startedAt uint64 | ||||||
|  |  | ||||||
| 	users       map[string]whatsapp.Contact | 	users       map[string]whatsapp.Contact | ||||||
| @@ -74,8 +69,6 @@ func (b *Bwhatsapp) Connect() error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.conn = conn | 	b.conn = conn | ||||||
| 	b.connExt = whatsappExt.ExtendConn(b.conn) |  | ||||||
| 	// TODO do we want to use it? b.connExt.SetClientName("Matterbridge WhatsApp bridge", "mb-wa") |  | ||||||
|  |  | ||||||
| 	b.conn.AddHandler(b) | 	b.conn.AddHandler(b) | ||||||
| 	b.Log.Debugln("WhatsApp connection successful") | 	b.Log.Debugln("WhatsApp connection successful") | ||||||
| @@ -89,7 +82,7 @@ func (b *Bwhatsapp) Connect() error { | |||||||
| 			b.Log.Debugln("Restoring WhatsApp session..") | 			b.Log.Debugln("Restoring WhatsApp session..") | ||||||
|  |  | ||||||
| 			// https://github.com/Rhymen/go-whatsapp#restore | 			// https://github.com/Rhymen/go-whatsapp#restore | ||||||
| 			session, err = b.conn.RestoreSession(session) | 			session, err = b.conn.RestoreWithSession(session) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				// TODO return or continue to normal login? | 				// TODO return or continue to normal login? | ||||||
| 				// restore session connection timed out (I couldn't get over it without logging in again) | 				// restore session connection timed out (I couldn't get over it without logging in again) | ||||||
| @@ -130,7 +123,7 @@ func (b *Bwhatsapp) Connect() error { | |||||||
| 		b.Log.Debug("Getting user avatars..") | 		b.Log.Debug("Getting user avatars..") | ||||||
|  |  | ||||||
| 		for jid := range b.users { | 		for jid := range b.users { | ||||||
| 			info, err := b.connExt.GetProfilePicThumb(jid) | 			info, err := b.GetProfilePicThumb(jid) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				b.Log.Warnf("Could not get profile photo of %s: %v", jid, err) | 				b.Log.Warnf("Could not get profile photo of %s: %v", jid, err) | ||||||
|  |  | ||||||
| @@ -294,7 +287,7 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) { | |||||||
| 	} | 	} | ||||||
| 	text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes)) | 	text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes)) | ||||||
|  |  | ||||||
| 	err := b.conn.Send(text) | 	_, err := b.conn.Send(text) | ||||||
|  |  | ||||||
| 	return text.Info.Id, err | 	return text.Info.Id, err | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,7 +2,9 @@ package bxmpp | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
|  | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| @@ -14,50 +16,31 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type Bxmpp struct { | type Bxmpp struct { | ||||||
| 	xc      *xmpp.Client |  | ||||||
| 	xmppMap map[string]string |  | ||||||
| 	*bridge.Config | 	*bridge.Config | ||||||
|  |  | ||||||
| 	startTime time.Time | 	startTime time.Time | ||||||
|  | 	xc        *xmpp.Client | ||||||
|  | 	xmppMap   map[string]string | ||||||
|  | 	connected bool | ||||||
|  | 	sync.RWMutex | ||||||
| } | } | ||||||
|  |  | ||||||
| func New(cfg *bridge.Config) bridge.Bridger { | func New(cfg *bridge.Config) bridge.Bridger { | ||||||
| 	b := &Bxmpp{Config: cfg} | 	return &Bxmpp{ | ||||||
| 	b.xmppMap = make(map[string]string) | 		Config:  cfg, | ||||||
| 	return b | 		xmppMap: make(map[string]string), | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) Connect() error { | func (b *Bxmpp) Connect() error { | ||||||
| 	var err error |  | ||||||
| 	b.Log.Infof("Connecting %s", b.GetString("Server")) | 	b.Log.Infof("Connecting %s", b.GetString("Server")) | ||||||
| 	b.xc, err = b.createXMPP() | 	if err := b.createXMPP(); err != nil { | ||||||
| 	if err != nil { |  | ||||||
| 		b.Log.Debugf("%#v", err) | 		b.Log.Debugf("%#v", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.Log.Info("Connection succeeded") | 	b.Log.Info("Connection succeeded") | ||||||
| 	go func() { | 	go b.manageConnection() | ||||||
| 		initial := true |  | ||||||
| 		bf := &backoff.Backoff{ |  | ||||||
| 			Min:    time.Second, |  | ||||||
| 			Max:    5 * time.Minute, |  | ||||||
| 			Jitter: true, |  | ||||||
| 		} |  | ||||||
| 		for { |  | ||||||
| 			if initial { |  | ||||||
| 				b.handleXMPP() |  | ||||||
| 				initial = false |  | ||||||
| 			} |  | ||||||
| 			d := bf.Duration() |  | ||||||
| 			b.Log.Infof("Disconnected. Reconnecting in %s", d) |  | ||||||
| 			time.Sleep(d) |  | ||||||
| 			b.xc, err = b.createXMPP() |  | ||||||
| 			if err == nil { |  | ||||||
| 				b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EventRejoinChannels} |  | ||||||
| 				b.handleXMPP() |  | ||||||
| 				bf.Reset() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -76,40 +59,58 @@ func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) Send(msg config.Message) (string, error) { | func (b *Bxmpp) Send(msg config.Message) (string, error) { | ||||||
|  | 	// should be fixed by using a cache instead of dropping | ||||||
|  | 	if !b.Connected() { | ||||||
|  | 		return "", fmt.Errorf("bridge %s not connected, dropping message %#v to bridge", b.Account, msg) | ||||||
|  | 	} | ||||||
| 	// ignore delete messages | 	// ignore delete messages | ||||||
| 	if msg.Event == config.EventMsgDelete { | 	if msg.Event == config.EventMsgDelete { | ||||||
| 		return "", nil | 		return "", nil | ||||||
| 	} | 	} | ||||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | 	b.Log.Debugf("=> Receiving %#v", msg) | ||||||
|  |  | ||||||
| 	// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support) | 	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support). | ||||||
| 	if msg.Extra != nil { | 	if msg.Extra != nil { | ||||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||||
| 			b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text}) | 			b.Log.Debugf("=> Sending attachement message %#v", rmsg) | ||||||
|  | 			if _, err := b.xc.Send(xmpp.Chat{ | ||||||
|  | 				Type:   "groupchat", | ||||||
|  | 				Remote: rmsg.Channel + "@" + b.GetString("Muc"), | ||||||
|  | 				Text:   rmsg.Username + rmsg.Text, | ||||||
|  | 			}); err != nil { | ||||||
|  | 				b.Log.WithError(err).Error("Unable to send message with share URL.") | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		if len(msg.Extra["file"]) > 0 { | 		if len(msg.Extra["file"]) > 0 { | ||||||
| 			return b.handleUploadFile(&msg) | 			return "", b.handleUploadFile(&msg) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var msgreplaceid string | 	var msgReplaceID string | ||||||
| 	msgid := xid.New().String() | 	msgID := xid.New().String() | ||||||
| 	if msg.ID != "" { | 	if msg.ID != "" { | ||||||
| 		msgid = msg.ID | 		msgID = msg.ID | ||||||
| 		msgreplaceid = msg.ID | 		msgReplaceID = msg.ID | ||||||
| 	} | 	} | ||||||
| 	// Post normal message | 	// Post normal message. | ||||||
| 	_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text, ID: msgid, ReplaceID: msgreplaceid}) | 	b.Log.Debugf("=> Sending message %#v", msg) | ||||||
| 	if err != nil { | 	if _, err := b.xc.Send(xmpp.Chat{ | ||||||
|  | 		Type:      "groupchat", | ||||||
|  | 		Remote:    msg.Channel + "@" + b.GetString("Muc"), | ||||||
|  | 		Text:      msg.Username + msg.Text, | ||||||
|  | 		ID:        msgID, | ||||||
|  | 		ReplaceID: msgReplaceID, | ||||||
|  | 	}); err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	return msgid, nil | 	return msgID, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) createXMPP() (*xmpp.Client, error) { | func (b *Bxmpp) createXMPP() error { | ||||||
| 	tc := new(tls.Config) | 	tc := &tls.Config{ | ||||||
| 	tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify") | 		ServerName:         strings.Split(b.GetString("Jid"), "@")[1], | ||||||
| 	tc.ServerName = strings.Split(b.GetString("Server"), ":")[0] | 		InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec | ||||||
|  | 	} | ||||||
| 	options := xmpp.Options{ | 	options := xmpp.Options{ | ||||||
| 		Host:                         b.GetString("Server"), | 		Host:                         b.GetString("Server"), | ||||||
| 		User:                         b.GetString("Jid"), | 		User:                         b.GetString("Jid"), | ||||||
| @@ -127,7 +128,54 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) { | |||||||
| 	} | 	} | ||||||
| 	var err error | 	var err error | ||||||
| 	b.xc, err = options.NewClient() | 	b.xc, err = options.NewClient() | ||||||
| 	return b.xc, err | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bxmpp) manageConnection() { | ||||||
|  | 	b.setConnected(true) | ||||||
|  | 	initial := true | ||||||
|  | 	bf := &backoff.Backoff{ | ||||||
|  | 		Min:    time.Second, | ||||||
|  | 		Max:    5 * time.Minute, | ||||||
|  | 		Jitter: true, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Main connection loop. Each iteration corresponds to a successful | ||||||
|  | 	// connection attempt and the subsequent handling of the connection. | ||||||
|  | 	for { | ||||||
|  | 		if initial { | ||||||
|  | 			initial = false | ||||||
|  | 		} else { | ||||||
|  | 			b.Remote <- config.Message{ | ||||||
|  | 				Username: "system", | ||||||
|  | 				Text:     "rejoin", | ||||||
|  | 				Channel:  "", | ||||||
|  | 				Account:  b.Account, | ||||||
|  | 				Event:    config.EventRejoinChannels, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := b.handleXMPP(); err != nil { | ||||||
|  | 			b.Log.WithError(err).Error("Disconnected.") | ||||||
|  | 			b.setConnected(false) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Reconnection loop using an exponential back-off strategy. We | ||||||
|  | 		// only break out of the loop if we have successfully reconnected. | ||||||
|  | 		for { | ||||||
|  | 			d := bf.Duration() | ||||||
|  | 			b.Log.Infof("Reconnecting in %s.", d) | ||||||
|  | 			time.Sleep(d) | ||||||
|  |  | ||||||
|  | 			b.Log.Infof("Reconnecting now.") | ||||||
|  | 			if err := b.createXMPP(); err == nil { | ||||||
|  | 				b.setConnected(true) | ||||||
|  | 				bf.Reset() | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			b.Log.Warn("Failed to reconnect.") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) xmppKeepAlive() chan bool { | func (b *Bxmpp) xmppKeepAlive() chan bool { | ||||||
| @@ -139,8 +187,7 @@ func (b *Bxmpp) xmppKeepAlive() chan bool { | |||||||
| 			select { | 			select { | ||||||
| 			case <-ticker.C: | 			case <-ticker.C: | ||||||
| 				b.Log.Debugf("PING") | 				b.Log.Debugf("PING") | ||||||
| 				err := b.xc.PingC2S("", "") | 				if err := b.xc.PingC2S("", ""); err != nil { | ||||||
| 				if err != nil { |  | ||||||
| 					b.Log.Debugf("PING failed %#v", err) | 					b.Log.Debugf("PING failed %#v", err) | ||||||
| 				} | 				} | ||||||
| 			case <-done: | 			case <-done: | ||||||
| @@ -152,31 +199,35 @@ func (b *Bxmpp) xmppKeepAlive() chan bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) handleXMPP() error { | func (b *Bxmpp) handleXMPP() error { | ||||||
| 	var ok bool |  | ||||||
| 	var msgid string |  | ||||||
| 	b.startTime = time.Now() | 	b.startTime = time.Now() | ||||||
|  |  | ||||||
| 	done := b.xmppKeepAlive() | 	done := b.xmppKeepAlive() | ||||||
| 	defer close(done) | 	defer close(done) | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		m, err := b.xc.Recv() | 		m, err := b.xc.Recv() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		switch v := m.(type) { | 		switch v := m.(type) { | ||||||
| 		case xmpp.Chat: | 		case xmpp.Chat: | ||||||
| 			if v.Type == "groupchat" { | 			if v.Type == "groupchat" { | ||||||
| 				b.Log.Debugf("== Receiving %#v", v) | 				b.Log.Debugf("== Receiving %#v", v) | ||||||
| 				event := "" |  | ||||||
| 				// skip invalid messages | 				// Skip invalid messages. | ||||||
| 				if b.skipMessage(v) { | 				if b.skipMessage(v) { | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | 				var event string | ||||||
| 				if strings.Contains(v.Text, "has set the subject to:") { | 				if strings.Contains(v.Text, "has set the subject to:") { | ||||||
| 					event = config.EventTopicChange | 					event = config.EventTopicChange | ||||||
| 				} | 				} | ||||||
| 				msgid = v.ID |  | ||||||
|  | 				msgID := v.ID | ||||||
| 				if v.ReplaceID != "" { | 				if v.ReplaceID != "" { | ||||||
| 					msgid = v.ReplaceID | 					msgID = v.ReplaceID | ||||||
| 				} | 				} | ||||||
| 				rmsg := config.Message{ | 				rmsg := config.Message{ | ||||||
| 					Username: b.parseNick(v.Remote), | 					Username: b.parseNick(v.Remote), | ||||||
| @@ -184,21 +235,23 @@ func (b *Bxmpp) handleXMPP() error { | |||||||
| 					Channel:  b.parseChannel(v.Remote), | 					Channel:  b.parseChannel(v.Remote), | ||||||
| 					Account:  b.Account, | 					Account:  b.Account, | ||||||
| 					UserID:   v.Remote, | 					UserID:   v.Remote, | ||||||
| 					ID:       msgid, | 					ID:       msgID, | ||||||
| 					Event:    event, | 					Event:    event, | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				// check if we have an action event | 				// Check if we have an action event. | ||||||
|  | 				var ok bool | ||||||
| 				rmsg.Text, ok = b.replaceAction(rmsg.Text) | 				rmsg.Text, ok = b.replaceAction(rmsg.Text) | ||||||
| 				if ok { | 				if ok { | ||||||
| 					rmsg.Event = config.EventUserAction | 					rmsg.Event = config.EventUserAction | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) | 				b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) | ||||||
| 				b.Log.Debugf("<= Message is %#v", rmsg) | 				b.Log.Debugf("<= Message is %#v", rmsg) | ||||||
| 				b.Remote <- rmsg | 				b.Remote <- rmsg | ||||||
| 			} | 			} | ||||||
| 		case xmpp.Presence: | 		case xmpp.Presence: | ||||||
| 			// do nothing | 			// Do nothing. | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -211,30 +264,41 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // handleUploadFile handles native upload of files | // handleUploadFile handles native upload of files | ||||||
| func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) { | func (b *Bxmpp) handleUploadFile(msg *config.Message) error { | ||||||
| 	var urldesc = "" | 	var urlDesc string | ||||||
|  |  | ||||||
| 	for _, f := range msg.Extra["file"] { | 	for _, file := range msg.Extra["file"] { | ||||||
| 		fi := f.(config.FileInfo) | 		fileInfo := file.(config.FileInfo) | ||||||
| 		if fi.Comment != "" { | 		if fileInfo.Comment != "" { | ||||||
| 			msg.Text += fi.Comment + ": " | 			msg.Text += fileInfo.Comment + ": " | ||||||
| 		} | 		} | ||||||
| 		if fi.URL != "" { | 		if fileInfo.URL != "" { | ||||||
| 			msg.Text = fi.URL | 			msg.Text = fileInfo.URL | ||||||
| 			if fi.Comment != "" { | 			if fileInfo.Comment != "" { | ||||||
| 				msg.Text = fi.Comment + ": " + fi.URL | 				msg.Text = fileInfo.Comment + ": " + fileInfo.URL | ||||||
| 				urldesc = fi.Comment | 				urlDesc = fileInfo.Comment | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text}) | 		if _, err := b.xc.Send(xmpp.Chat{ | ||||||
| 		if err != nil { | 			Type:   "groupchat", | ||||||
| 			return "", err | 			Remote: msg.Channel + "@" + b.GetString("Muc"), | ||||||
|  | 			Text:   msg.Username + msg.Text, | ||||||
|  | 		}); err != nil { | ||||||
|  | 			return err | ||||||
| 		} | 		} | ||||||
| 		if fi.URL != "" { |  | ||||||
| 			b.xc.SendOOB(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Ooburl: fi.URL, Oobdesc: urldesc}) | 		if fileInfo.URL != "" { | ||||||
|  | 			if _, err := b.xc.SendOOB(xmpp.Chat{ | ||||||
|  | 				Type:    "groupchat", | ||||||
|  | 				Remote:  msg.Channel + "@" + b.GetString("Muc"), | ||||||
|  | 				Ooburl:  fileInfo.URL, | ||||||
|  | 				Oobdesc: urlDesc, | ||||||
|  | 			}); err != nil { | ||||||
|  | 				b.Log.WithError(err).Warn("Failed to send share URL.") | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "", nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bxmpp) parseNick(remote string) string { | func (b *Bxmpp) parseNick(remote string) string { | ||||||
| @@ -279,6 +343,17 @@ func (b *Bxmpp) skipMessage(message xmpp.Chat) bool { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// skip delayed messages | 	// skip delayed messages | ||||||
| 	t := time.Time{} | 	return !message.Stamp.IsZero() && time.Since(message.Stamp).Minutes() > 5 | ||||||
| 	return message.Stamp != t | } | ||||||
|  |  | ||||||
|  | func (b *Bxmpp) setConnected(state bool) { | ||||||
|  | 	b.Lock() | ||||||
|  | 	b.connected = state | ||||||
|  | 	defer b.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bxmpp) Connected() bool { | ||||||
|  | 	b.RLock() | ||||||
|  | 	defer b.RUnlock() | ||||||
|  | 	return b.connected | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,3 +1,73 @@ | |||||||
|  | # dev | ||||||
|  |  | ||||||
|  | # v1.15.1 | ||||||
|  | ## Enhancements | ||||||
|  | * discord: Support bulk deletions #851 | ||||||
|  | * discord: Support channels in categories #863 (use category/channel. See matterbridge.toml.sample for more info) | ||||||
|  | * mattermost: Add an option to skip the Mattermost server version check #849 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  | * xmpp: fix segfault when disconnected/reconnected #856 | ||||||
|  | * telegram: fix panic in handleEntities #858 | ||||||
|  |  | ||||||
|  | # v1.15.0 | ||||||
|  | ## New features | ||||||
|  | * Add scripting (tengo) support for every outgoing message (#806) | ||||||
|  |   See https://github.com/42wim/matterbridge/wiki/Settings#tengo and  | ||||||
|  |   https://github.com/42wim/matterbridge/wiki/Settings#outmessage for more information | ||||||
|  | * Add tengo support to RemoteNickFormat (#793) | ||||||
|  |   See https://github.com/42wim/matterbridge/wiki/Settings#remotenickformat-2 | ||||||
|  | * Deprecated `Message` under `[tengo]` to `InMessage` | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  | * general: Forward only user-typing messages if supported by protocol (#832) | ||||||
|  | * general: updated wiki with all possible settings: https://github.com/42wim/matterbridge/wiki/Settings | ||||||
|  | * tengo: Add msg event to tengo | ||||||
|  | * xmpp: Verify TLS against JID domain, not the host. (xmpp) (#834) | ||||||
|  | * xmpp: Allow messages with timestamp (xmpp). Fixes #835 (#847) | ||||||
|  | * irc: Add verbose IRC joins/parts (ident@host) (#805) | ||||||
|  |   See https://github.com/42wim/matterbridge/wiki/Settings#verbosejoinpart | ||||||
|  | * rocketchat: Add useraction support (rocketchat). Closes #772 (#794) | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  | * slack: Fix regression in autojoining with legacy tokens (slack). Fixes #651 (#848) | ||||||
|  | * xmpp: Revert xmpp to orig behaviour. Closes #844 | ||||||
|  | * whatsapp: Update github.com/Rhymen/go-whatsapp vendor. Fixes #843 | ||||||
|  | * mattermost: Update channels of all teams (mattermost) | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @42wim, @Helcaraxan, @chotaire, @qaisjp, @dajohi, @kousu | ||||||
|  |  | ||||||
|  | # v1.14.4 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  | * mattermost: Add Id to EditMessage (mattermost). Fixes #802 | ||||||
|  | * mattermost: Fix panic on nil message.Post (mattermost). Fixes #804 | ||||||
|  | * mattermost: Handle unthreaded messages (mattermost). Fixes #803 | ||||||
|  | * mattermost: Use paging in initUser and UpdateUsers (mattermost) | ||||||
|  | * slack: Add lacking clean-up in Slack synchronisation (#811) | ||||||
|  | * slack: Disable user lookups on delete messages (slack) (#812) | ||||||
|  |  | ||||||
|  | # v1.14.3 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  | * irc: Fix deadlock on reconnect (irc). Closes #757 | ||||||
|  |  | ||||||
|  | # v1.14.2 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  | * general: Update tengo vendor and load the stdlib. Fixes #789 (#792) | ||||||
|  | * rocketchat: Look up #channel too (rocketchat). Fix #773 (#775) | ||||||
|  | * slack: Ignore messagereplied and hidden messages (slack). Fixes #709 (#779) | ||||||
|  | * telegram: Handle nil message (telegram). Fixes #777 | ||||||
|  | * irc: Use default nick if none specified (irc). Fixes #785 | ||||||
|  | * irc: Return when not connected and drop a message (irc). Fixes #786 | ||||||
|  | * irc: Revert fix for #722 (Support quits from irc correctly). Closes #781 | ||||||
|  |  | ||||||
|  | ## Contributors | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @42wim, @Helcaraxan, @dajohi | ||||||
|  |  | ||||||
| # v1.14.1 | # v1.14.1 | ||||||
| ## Bugfix | ## Bugfix | ||||||
| * slack: Fix crash double unlock (slack) (#771) | * slack: Fix crash double unlock (slack) (#771) | ||||||
|   | |||||||
| @@ -1,5 +1,8 @@ | |||||||
| #!/bin/bash | #!/usr/bin/env bash | ||||||
| go version | grep go1.11 || exit | set -u -e -x -o pipefail | ||||||
|  |  | ||||||
|  | go version | grep go1.12 || exit | ||||||
|  |  | ||||||
| VERSION=$(git describe --tags) | VERSION=$(git describe --tags) | ||||||
| mkdir ci/binaries | mkdir ci/binaries | ||||||
| GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe | GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								ci/lint.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								ci/lint.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  | set -u -e -x -o pipefail | ||||||
|  |  | ||||||
|  | if [[ -n "${GOLANGCI_VERSION-}" ]]; then | ||||||
|  |   # Retrieve the golangci-lint linter binary. | ||||||
|  |   curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION} | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Run the linter. | ||||||
|  | golangci-lint run | ||||||
|  |  | ||||||
|  | if [[ "${GO111MODULE-off}" == "on" ]]; then | ||||||
|  |   # If Go modules are active then check that dependencies are correctly maintained. | ||||||
|  |   go mod tidy | ||||||
|  |   go mod vendor | ||||||
|  |   git diff --exit-code --quiet || (echo "Please run 'go mod tidy' to clean up the 'go.mod' and 'go.sum' files."; false) | ||||||
|  | fi | ||||||
							
								
								
									
										17
									
								
								ci/test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								ci/test.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  | set -u -e -x -o pipefail | ||||||
|  |  | ||||||
|  | if [[ -n "${REPORT_COVERAGE+cover}" ]]; then | ||||||
|  |   # Retrieve and prepare CodeClimate's test coverage reporter. | ||||||
|  |   curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter | ||||||
|  |   chmod +x ./cc-test-reporter | ||||||
|  |   ./cc-test-reporter before-build | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Run all the tests with the race detector and generate coverage. | ||||||
|  | go test -v -race -coverprofile c.out ./... | ||||||
|  |  | ||||||
|  | if [[ -n "${REPORT_COVERAGE+cover}" && "${TRAVIS_SECURE_ENV_VARS}" == "true" ]]; then | ||||||
|  |   # Upload test coverage to CodeClimate. | ||||||
|  |   ./cc-test-reporter after-build | ||||||
|  | fi | ||||||
							
								
								
									
										7
									
								
								contrib/outmessage-irccolors.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								contrib/outmessage-irccolors.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | // See https://github.com/42wim/matterbridge/issues/798 | ||||||
|  |  | ||||||
|  | // if we're not sending to an irc bridge we strip the IRC colors | ||||||
|  | if outProtocol != "irc" { | ||||||
|  |     re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`) | ||||||
|  |     msgText=re.replace(msgText,"") | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								contrib/remotenickformat-zerowidth.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								contrib/remotenickformat-zerowidth.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | /* | ||||||
|  | This script will return the nick except with multi-character usernames | ||||||
|  | containing a zero-width space between the first and second character letter. | ||||||
|  |  | ||||||
|  | Single character usernames will be left untouched. | ||||||
|  |  | ||||||
|  | This is useful to prevent remote users from nickalerting | ||||||
|  | IRC users of the same name when the remote user speaks. | ||||||
|  |  | ||||||
|  | This result can be used in {TENGO} in RemoteNickFormat. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | result = nick | ||||||
|  | if len(nick) > 1 { | ||||||
|  |     result = string(nick[0]) + "" + nick[1:] | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								contrib/remotenickformat.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contrib/remotenickformat.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | /* | ||||||
|  | This script will return the current time in kitchen format if the protocol (of the remote bridge) isn't irc | ||||||
|  | See https://github.com/d5/tengo/blob/master/docs/stdlib-times.md | ||||||
|  | This result can be used in {TENGO} in RemoteNickFormat | ||||||
|  | */ | ||||||
|  | times := import("times") | ||||||
|  | if protocol != "irc" { | ||||||
|  |    result=times.time_format(times.now(),times.format_kitchen) | ||||||
|  | } | ||||||
| @@ -3,35 +3,41 @@ package bridgemap | |||||||
| import ( | import ( | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| 	"github.com/42wim/matterbridge/bridge/api" | 	"github.com/42wim/matterbridge/bridge/api" | ||||||
| 	"github.com/42wim/matterbridge/bridge/discord" | 	bdiscord "github.com/42wim/matterbridge/bridge/discord" | ||||||
| 	"github.com/42wim/matterbridge/bridge/gitter" | 	bgitter "github.com/42wim/matterbridge/bridge/gitter" | ||||||
| 	"github.com/42wim/matterbridge/bridge/irc" | 	birc "github.com/42wim/matterbridge/bridge/irc" | ||||||
| 	"github.com/42wim/matterbridge/bridge/matrix" | 	bmatrix "github.com/42wim/matterbridge/bridge/matrix" | ||||||
| 	"github.com/42wim/matterbridge/bridge/mattermost" | 	bmattermost "github.com/42wim/matterbridge/bridge/mattermost" | ||||||
| 	"github.com/42wim/matterbridge/bridge/rocketchat" | 	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat" | ||||||
| 	"github.com/42wim/matterbridge/bridge/slack" | 	bslack "github.com/42wim/matterbridge/bridge/slack" | ||||||
| 	"github.com/42wim/matterbridge/bridge/sshchat" | 	bsshchat "github.com/42wim/matterbridge/bridge/sshchat" | ||||||
| 	"github.com/42wim/matterbridge/bridge/steam" | 	bsteam "github.com/42wim/matterbridge/bridge/steam" | ||||||
| 	"github.com/42wim/matterbridge/bridge/telegram" | 	btelegram "github.com/42wim/matterbridge/bridge/telegram" | ||||||
| 	"github.com/42wim/matterbridge/bridge/whatsapp" | 	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp" | ||||||
| 	"github.com/42wim/matterbridge/bridge/xmpp" | 	bxmpp "github.com/42wim/matterbridge/bridge/xmpp" | ||||||
| 	"github.com/42wim/matterbridge/bridge/zulip" | 	bzulip "github.com/42wim/matterbridge/bridge/zulip" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var FullMap = map[string]bridge.Factory{ | var ( | ||||||
| 	"api":          api.New, | 	FullMap = map[string]bridge.Factory{ | ||||||
| 	"discord":      bdiscord.New, | 		"api":          api.New, | ||||||
| 	"gitter":       bgitter.New, | 		"discord":      bdiscord.New, | ||||||
| 	"irc":          birc.New, | 		"gitter":       bgitter.New, | ||||||
| 	"mattermost":   bmattermost.New, | 		"irc":          birc.New, | ||||||
| 	"matrix":       bmatrix.New, | 		"mattermost":   bmattermost.New, | ||||||
| 	"rocketchat":   brocketchat.New, | 		"matrix":       bmatrix.New, | ||||||
| 	"slack-legacy": bslack.NewLegacy, | 		"rocketchat":   brocketchat.New, | ||||||
| 	"slack":        bslack.New, | 		"slack-legacy": bslack.NewLegacy, | ||||||
| 	"sshchat":      bsshchat.New, | 		"slack":        bslack.New, | ||||||
| 	"steam":        bsteam.New, | 		"sshchat":      bsshchat.New, | ||||||
| 	"telegram":     btelegram.New, | 		"steam":        bsteam.New, | ||||||
| 	"whatsapp":     bwhatsapp.New, | 		"telegram":     btelegram.New, | ||||||
| 	"xmpp":         bxmpp.New, | 		"whatsapp":     bwhatsapp.New, | ||||||
| 	"zulip":        bzulip.New, | 		"xmpp":         bxmpp.New, | ||||||
| } | 		"zulip":        bzulip.New, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	UserTypingSupport = map[string]struct{}{ | ||||||
|  | 		"slack": {}, | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|   | |||||||
| @@ -9,7 +9,9 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/42wim/matterbridge/internal" | ||||||
| 	"github.com/d5/tengo/script" | 	"github.com/d5/tengo/script" | ||||||
|  | 	"github.com/d5/tengo/stdlib" | ||||||
| 	lru "github.com/hashicorp/golang-lru" | 	lru "github.com/hashicorp/golang-lru" | ||||||
| 	"github.com/peterhellberg/emojilib" | 	"github.com/peterhellberg/emojilib" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| @@ -211,23 +213,6 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con | |||||||
| 		return channels | 		return channels | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// irc quit is for the whole bridge, isn't a per channel quit. |  | ||||||
| 	// channel is empty when we quit |  | ||||||
| 	if msg.Event == config.EventJoinLeave && getProtocol(msg) == "irc" && msg.Channel == "" { |  | ||||||
| 		// if we only have one channel on this irc bridge it's got to be the sending one. |  | ||||||
| 		// don't send it back |  | ||||||
| 		if dest.Account == msg.Account && len(dest.Channels) == 1 && dest.Protocol == "irc" { |  | ||||||
| 			return channels |  | ||||||
| 		} |  | ||||||
| 		for _, channel := range gw.Channels { |  | ||||||
| 			if channel.Account == dest.Account && strings.Contains(channel.Direction, "out") && |  | ||||||
| 				gw.validGatewayDest(msg) { |  | ||||||
| 				channels = append(channels, *channel) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return channels |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// if source channel is in only, do nothing | 	// if source channel is in only, do nothing | ||||||
| 	for _, channel := range gw.Channels { | 	for _, channel := range gw.Channels { | ||||||
| 		// lookup the channel from the message | 		// lookup the channel from the message | ||||||
| @@ -347,6 +332,11 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri | |||||||
| 	nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1) | 	nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1) | ||||||
| 	nick = strings.Replace(nick, "{NICK}", msg.Username, -1) | 	nick = strings.Replace(nick, "{NICK}", msg.Username, -1) | ||||||
| 	nick = strings.Replace(nick, "{CHANNEL}", msg.Channel, -1) | 	nick = strings.Replace(nick, "{CHANNEL}", msg.Channel, -1) | ||||||
|  | 	tengoNick, err := gw.modifyUsernameTengo(msg, br) | ||||||
|  | 	if err != nil { | ||||||
|  | 		gw.logger.Errorf("modifyUsernameTengo error: %s", err) | ||||||
|  | 	} | ||||||
|  | 	nick = strings.Replace(nick, "{TENGO}", tengoNick, -1) //nolint:gocritic | ||||||
| 	return nick | 	return nick | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -363,6 +353,9 @@ func (gw *Gateway) modifyMessage(msg *config.Message) { | |||||||
| 	if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil { | 	if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil { | ||||||
| 		gw.logger.Errorf("TengoModifyMessage failed: %s", err) | 		gw.logger.Errorf("TengoModifyMessage failed: %s", err) | ||||||
| 	} | 	} | ||||||
|  | 	if err := modifyMessageTengo(gw.BridgeValues().Tengo.Message, msg); err != nil { | ||||||
|  | 		gw.logger.Errorf("Tengo.Message failed: %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// replace :emoji: to unicode | 	// replace :emoji: to unicode | ||||||
| 	msg.Text = emojilib.Replace(msg.Text) | 	msg.Text = emojilib.Replace(msg.Text) | ||||||
| @@ -437,6 +430,11 @@ func (gw *Gateway) SendMessage( | |||||||
| 		msg.ParentID = "msg-parent-not-found" | 		msg.ParentID = "msg-parent-not-found" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	err := gw.modifySendMessageTengo(rmsg, &msg, dest) | ||||||
|  | 	if err != nil { | ||||||
|  | 		gw.logger.Errorf("modifySendMessageTengo: %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// if we are using mattermost plugin account, send messages to MattermostPlugin channel | 	// if we are using mattermost plugin account, send messages to MattermostPlugin channel | ||||||
| 	// that can be picked up by the mattermost matterbridge plugin | 	// that can be picked up by the mattermost matterbridge plugin | ||||||
| 	if dest.Account == "mattermost.plugin" { | 	if dest.Account == "mattermost.plugin" { | ||||||
| @@ -503,6 +501,7 @@ func modifyMessageTengo(filename string, msg *config.Message) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	s := script.New(res) | 	s := script.New(res) | ||||||
|  | 	s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) | ||||||
| 	_ = s.Add("msgText", msg.Text) | 	_ = s.Add("msgText", msg.Text) | ||||||
| 	_ = s.Add("msgUsername", msg.Username) | 	_ = s.Add("msgUsername", msg.Username) | ||||||
| 	_ = s.Add("msgAccount", msg.Account) | 	_ = s.Add("msgAccount", msg.Account) | ||||||
| @@ -518,3 +517,77 @@ func modifyMessageTengo(filename string, msg *config.Message) error { | |||||||
| 	msg.Username = c.Get("msgUsername").String() | 	msg.Username = c.Get("msgUsername").String() | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) (string, error) { | ||||||
|  | 	filename := gw.BridgeValues().Tengo.RemoteNickFormat | ||||||
|  | 	if filename == "" { | ||||||
|  | 		return "", nil | ||||||
|  | 	} | ||||||
|  | 	res, err := ioutil.ReadFile(filename) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	s := script.New(res) | ||||||
|  | 	s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) | ||||||
|  | 	_ = s.Add("result", "") | ||||||
|  | 	_ = s.Add("msgText", msg.Text) | ||||||
|  | 	_ = s.Add("msgUsername", msg.Username) | ||||||
|  | 	_ = s.Add("nick", msg.Username) | ||||||
|  | 	_ = s.Add("msgAccount", msg.Account) | ||||||
|  | 	_ = s.Add("msgChannel", msg.Channel) | ||||||
|  | 	_ = s.Add("channel", msg.Channel) | ||||||
|  | 	_ = s.Add("msgProtocol", msg.Protocol) | ||||||
|  | 	_ = s.Add("remoteAccount", br.Account) | ||||||
|  | 	_ = s.Add("protocol", br.Protocol) | ||||||
|  | 	_ = s.Add("bridge", br.Name) | ||||||
|  | 	_ = s.Add("gateway", gw.Name) | ||||||
|  | 	c, err := s.Compile() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	if err := c.Run(); err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return c.Get("result").String(), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.Message, br *bridge.Bridge) error { | ||||||
|  | 	filename := gw.BridgeValues().Tengo.OutMessage | ||||||
|  | 	var res []byte | ||||||
|  | 	var err error | ||||||
|  | 	if filename == "" { | ||||||
|  | 		res, err = internal.Asset("tengo/outmessage.tengo") | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		res, err = ioutil.ReadFile(filename) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	s := script.New(res) | ||||||
|  | 	s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) | ||||||
|  | 	_ = s.Add("inAccount", origmsg.Account) | ||||||
|  | 	_ = s.Add("inProtocol", origmsg.Protocol) | ||||||
|  | 	_ = s.Add("inChannel", origmsg.Channel) | ||||||
|  | 	_ = s.Add("inGateway", origmsg.Gateway) | ||||||
|  | 	_ = s.Add("inEvent", origmsg.Event) | ||||||
|  | 	_ = s.Add("outAccount", br.Account) | ||||||
|  | 	_ = s.Add("outProtocol", br.Protocol) | ||||||
|  | 	_ = s.Add("outChannel", msg.Channel) | ||||||
|  | 	_ = s.Add("outGateway", gw.Name) | ||||||
|  | 	_ = s.Add("outEvent", msg.Event) | ||||||
|  | 	_ = s.Add("msgText", msg.Text) | ||||||
|  | 	_ = s.Add("msgUsername", msg.Username) | ||||||
|  | 	c, err := s.Compile() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := c.Run(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	msg.Text = c.Get("msgText").String() | ||||||
|  | 	msg.Username = c.Get("msgUsername").String() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/42wim/matterbridge/gateway/bridgemap" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // handleEventFailure handles failures and reconnects bridges. | // handleEventFailure handles failures and reconnects bridges. | ||||||
| @@ -190,6 +191,14 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool { | |||||||
| func (gw *Gateway) handleMessage(rmsg *config.Message, dest *bridge.Bridge) []*BrMsgID { | func (gw *Gateway) handleMessage(rmsg *config.Message, dest *bridge.Bridge) []*BrMsgID { | ||||||
| 	var brMsgIDs []*BrMsgID | 	var brMsgIDs []*BrMsgID | ||||||
|  |  | ||||||
|  | 	// Not all bridges support "user is typing" indications so skip the message | ||||||
|  | 	// if the targeted bridge does not support it. | ||||||
|  | 	if rmsg.Event == config.EventUserTyping { | ||||||
|  | 		if _, ok := bridgemap.UserTypingSupport[dest.Protocol]; !ok { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// if we have an attached file, or other info | 	// if we have an attached file, or other info | ||||||
| 	if rmsg.Extra != nil && len(rmsg.Extra[config.EventFileFailureSize]) != 0 && rmsg.Text == "" { | 	if rmsg.Extra != nil && len(rmsg.Extra[config.EventFileFailureSize]) != 0 && rmsg.Text == "" { | ||||||
| 		return brMsgIDs | 		return brMsgIDs | ||||||
|   | |||||||
| @@ -125,7 +125,8 @@ func (r *Router) handleReceive() { | |||||||
| 		r.handleEventGetChannelMembers(&msg) | 		r.handleEventGetChannelMembers(&msg) | ||||||
| 		r.handleEventFailure(&msg) | 		r.handleEventFailure(&msg) | ||||||
| 		r.handleEventRejoinChannels(&msg) | 		r.handleEventRejoinChannels(&msg) | ||||||
| 		idx := 0 |  | ||||||
|  | 		filesHandled := false | ||||||
| 		for _, gw := range r.Gateways { | 		for _, gw := range r.Gateways { | ||||||
| 			// record all the message ID's of the different bridges | 			// record all the message ID's of the different bridges | ||||||
| 			var msgIDs []*BrMsgID | 			var msgIDs []*BrMsgID | ||||||
| @@ -134,17 +135,26 @@ func (r *Router) handleReceive() { | |||||||
| 			} | 			} | ||||||
| 			msg.Timestamp = time.Now() | 			msg.Timestamp = time.Now() | ||||||
| 			gw.modifyMessage(&msg) | 			gw.modifyMessage(&msg) | ||||||
| 			if idx == 0 { | 			if !filesHandled { | ||||||
| 				gw.handleFiles(&msg) | 				gw.handleFiles(&msg) | ||||||
|  | 				filesHandled = true | ||||||
| 			} | 			} | ||||||
| 			for _, br := range gw.Bridges { | 			for _, br := range gw.Bridges { | ||||||
| 				msgIDs = append(msgIDs, gw.handleMessage(&msg, br)...) | 				msgIDs = append(msgIDs, gw.handleMessage(&msg, br)...) | ||||||
| 			} | 			} | ||||||
| 			// only add the message ID if it doesn't already exists |  | ||||||
| 			if _, ok := gw.Messages.Get(msg.Protocol + " " + msg.ID); !ok && msg.ID != "" { | 			if msg.ID != "" { | ||||||
| 				gw.Messages.Add(msg.Protocol+" "+msg.ID, msgIDs) | 				_, exists := gw.Messages.Get(msg.Protocol + " " + msg.ID) | ||||||
|  |  | ||||||
|  | 				// Only add the message ID if it doesn't already exist | ||||||
|  | 				// | ||||||
|  | 				// For some bridges we always add/update the message ID. | ||||||
|  | 				// This is necessary as msgIDs will change if a bridge returns | ||||||
|  | 				// a different ID in response to edits. | ||||||
|  | 				if !exists || msg.Protocol == "discord" { | ||||||
|  | 					gw.Messages.Add(msg.Protocol+" "+msg.ID, msgIDs) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			idx++ |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,36 +3,32 @@ module github.com/42wim/matterbridge | |||||||
| require ( | require ( | ||||||
| 	github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 | 	github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 | ||||||
| 	github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f | 	github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f | ||||||
| 	github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect |  | ||||||
| 	github.com/Jeffail/gabs v1.1.1 // indirect | 	github.com/Jeffail/gabs v1.1.1 // indirect | ||||||
| 	github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 | 	github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 | ||||||
|  | 	github.com/Rhymen/go-whatsapp v0.0.2 | ||||||
| 	github.com/bwmarrin/discordgo v0.19.0 | 	github.com/bwmarrin/discordgo v0.19.0 | ||||||
| 	github.com/d5/tengo v1.12.1 | 	github.com/d5/tengo v1.24.1 | ||||||
| 	github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec | 	github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec | ||||||
| 	github.com/fsnotify/fsnotify v1.4.7 | 	github.com/fsnotify/fsnotify v1.4.7 | ||||||
| 	github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible | 	github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible | ||||||
| 	github.com/google/gops v0.3.5 | 	github.com/google/gops v0.3.6 | ||||||
| 	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect | 	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect | ||||||
| 	github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect | 	github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect | ||||||
| 	github.com/gorilla/schema v1.0.2 | 	github.com/gorilla/schema v1.1.0 | ||||||
| 	github.com/gorilla/websocket v1.4.0 | 	github.com/gorilla/websocket v1.4.0 | ||||||
| 	github.com/hashicorp/golang-lru v0.5.0 | 	github.com/hashicorp/golang-lru v0.5.1 | ||||||
| 	github.com/hpcloud/tail v1.0.0 // indirect | 	github.com/hpcloud/tail v1.0.0 // indirect | ||||||
| 	github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 | 	github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 | ||||||
| 	github.com/jtolds/gls v4.2.1+incompatible // indirect | 	github.com/jtolds/gls v4.2.1+incompatible // indirect | ||||||
| 	github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect | 	github.com/labstack/echo/v4 v4.1.6 | ||||||
| 	github.com/kr/pretty v0.1.0 // indirect |  | ||||||
| 	github.com/labstack/echo/v4 v4.0.0 |  | ||||||
| 	github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 | 	github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 | ||||||
| 	github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect | 	github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect | ||||||
| 	github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect | 	github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect | ||||||
| 	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d | 	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d | ||||||
| 	github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b |  | ||||||
| 	github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 | 	github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 | ||||||
| 	github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea | 	github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea | ||||||
| 	github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 | 	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.0.0-20180806162718-01618749af61 | ||||||
| 	github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e |  | ||||||
| 	github.com/mattermost/mattermost-server v5.5.0+incompatible | 	github.com/mattermost/mattermost-server v5.5.0+incompatible | ||||||
| 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect | 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect | ||||||
| 	github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect | 	github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect | ||||||
| @@ -42,32 +38,28 @@ require ( | |||||||
| 	github.com/nlopes/slack v0.5.0 | 	github.com/nlopes/slack v0.5.0 | ||||||
| 	github.com/onsi/ginkgo v1.6.0 // indirect | 	github.com/onsi/ginkgo v1.6.0 // indirect | ||||||
| 	github.com/onsi/gomega v1.4.1 // indirect | 	github.com/onsi/gomega v1.4.1 // indirect | ||||||
| 	github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83 | 	github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c | ||||||
| 	github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect | 	github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect | ||||||
| 	github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 | 	github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 | ||||||
| 	github.com/pkg/errors v0.8.0 // indirect |  | ||||||
| 	github.com/rs/xid v1.2.1 | 	github.com/rs/xid v1.2.1 | ||||||
| 	github.com/russross/blackfriday v1.5.2 | 	github.com/russross/blackfriday v1.5.2 | ||||||
| 	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca | 	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca | ||||||
| 	github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 | 	github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 | ||||||
| 	github.com/sirupsen/logrus v1.3.0 | 	github.com/sirupsen/logrus v1.4.2 | ||||||
| 	github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect | 	github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect | ||||||
| 	github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect | 	github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect | ||||||
| 	github.com/spf13/viper v1.3.1 | 	github.com/spf13/viper v1.4.0 | ||||||
| 	github.com/stretchr/testify v1.3.0 | 	github.com/stretchr/testify v1.3.0 | ||||||
| 	github.com/technoweenie/multipartstreamer v1.0.1 // indirect | 	github.com/technoweenie/multipartstreamer v1.0.1 // indirect | ||||||
| 	github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect | 	github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect | ||||||
| 	github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 | 	github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 | ||||||
| 	gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect | 	gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect | ||||||
| 	gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect | 	gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect | ||||||
| 	gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f | 	gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f | ||||||
| 	gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 // indirect | 	gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 // indirect | ||||||
| 	gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe // indirect | 	gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe // indirect | ||||||
| 	gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 // indirect | 	gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 // indirect | ||||||
| 	go.uber.org/atomic v1.3.2 // indirect | 	golang.org/x/image v0.0.0-20190616094056-33659d3de4f5 | ||||||
| 	go.uber.org/multierr v1.1.0 // indirect |  | ||||||
| 	go.uber.org/zap v1.9.1 // indirect |  | ||||||
| 	golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 |  | ||||||
| 	gopkg.in/fsnotify.v1 v1.4.7 // indirect | 	gopkg.in/fsnotify.v1 v1.4.7 // indirect | ||||||
| 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | ||||||
| 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect | 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect | ||||||
|   | |||||||
							
								
								
									
										220
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										220
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,22 +1,40 @@ | |||||||
|  | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||||
| github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg= | 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/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 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/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= | ||||||
| github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 h1:v/zr4ns/4sSahF9KBm4Uc933bLsEEv7LuT63CJ019yo= | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||||
| github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E= | 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/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.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY= | github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY= | ||||||
| github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg= | github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/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.0.2 h1:MelwdquHuuNObBGV7CpXbky2aVdilx/CwiXMwZvS74U= | ||||||
|  | github.com/Rhymen/go-whatsapp v0.0.2/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg= | ||||||
|  | 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/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/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE= | 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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||||
|  | 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/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY= | github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY= | ||||||
| github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= | github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= | ||||||
|  | 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/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | ||||||
| github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= |  | ||||||
| github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= | ||||||
| github.com/d5/tengo v1.12.1 h1:libKkDM95CsZgYs6E5eiEaM9sbcw2EzJRSkr9o5NO4s= | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||||||
| github.com/d5/tengo v1.12.1/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY= | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= | ||||||
|  | github.com/d5/tengo v1.24.1 h1:b+epGF5Qi0XUkYUUl8y6hVzLxg/eu9FYUAdb4H/KieY= | ||||||
|  | github.com/d5/tengo v1.24.1/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY= | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| @@ -24,50 +42,74 @@ github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX | |||||||
| github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY= | 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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||||||
| github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= | ||||||
| github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | 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.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||||||
|  | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||||
|  | 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/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 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-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= | ||||||
| github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= | 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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw= | github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= | ||||||
| github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0= | 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/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/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to= | ||||||
|  | github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI= | ||||||
| github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo= | 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/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA= | ||||||
| github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ= | github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ= | ||||||
| github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||||
| github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= | ||||||
| github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= | ||||||
| github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA= |  | ||||||
| github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= |  | ||||||
| github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= | 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.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||||
| github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | ||||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | 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.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= | ||||||
|  | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | 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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | ||||||
| github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= | 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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= | ||||||
| github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||||||
| github.com/jessevdk/go-flags v1.3.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | github.com/jessevdk/go-flags v1.3.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | ||||||
|  | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= | ||||||
| github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= | github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= | ||||||
| github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= | github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= | ||||||
| github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= | github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= | ||||||
| github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | github.com/jtolds/gls v4.2.1+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 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= | ||||||
| github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= | github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= | ||||||
|  | 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 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.1/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | ||||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | 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/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||||
| github.com/labstack/echo/v4 v4.0.0 h1:q1GH+caIXPP7H2StPIdzy/ez9CO0EepqYeUg6vi9SWM= | github.com/labstack/echo/v4 v4.1.6 h1:WOvLa4T1KzWCRpANwz0HGgWDelXSSGwIKtKBbFdHTv4= | ||||||
| github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno= | github.com/labstack/echo/v4 v4.1.6/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE= | ||||||
| github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0= | github.com/labstack/gommon v0.2.9 h1:heVeuAYtevIQVYkGj6A41dtfT91LrvFG220lavpWhrU= | ||||||
| github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= | github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4= | ||||||
| github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 h1:a40kRmhA1p2XFJ6gqXfCExSyuDDCp/U9LA8ZY27u2Lk= | github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 h1:a40kRmhA1p2XFJ6gqXfCExSyuDDCp/U9LA8ZY27u2Lk= | ||||||
| github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= | github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= | ||||||
| github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU= | github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU= | ||||||
| @@ -78,8 +120,6 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe | |||||||
| github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | github.com/magiconair/properties v1.8.0/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 h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs= | ||||||
| github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= | github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= | ||||||
| github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b h1:cO6Z+yj4Ivq/ay/IxSrV90oSIW/SSXWLa+XHsiLKMrw= |  | ||||||
| github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b/go.mod h1:dW19fYkkdUZsBAx7zv9fDh0n6NRqYIaKwB2JEBw8d0U= |  | ||||||
| github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k= | 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/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q= | ||||||
| github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc= | github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc= | ||||||
| @@ -88,17 +128,17 @@ github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtW | |||||||
| github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA= | 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 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE= | ||||||
| github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU= | github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU= | ||||||
| github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e h1:1NqciL8sz+0UYeFrd/UQlL8tJPhFxOBmg+a94DN2sJU= |  | ||||||
| github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e/go.mod h1:DrIFGcFumRlEW5k3PJjWGKPd4+w37d3SwOxlh1ZAL+4= |  | ||||||
| github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU= | 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/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y= | ||||||
| github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= |  | ||||||
| github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= | 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-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= | ||||||
| github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | 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-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= | github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= | ||||||
| github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||||
| github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | 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/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 h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= | ||||||
| github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= | ||||||
| github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= | ||||||
| @@ -107,48 +147,65 @@ github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteT | |||||||
| github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= | 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= | github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9DtP8gnTmNtMxbPyhYltfxsITel8g= | ||||||
| github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E= | github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E= | ||||||
|  | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | ||||||
| github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 h1:mp6tU1r0xLostUGLkTspf/9/AiHuVD7ptyXhySkDEsE= | github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 h1:mp6tU1r0xLostUGLkTspf/9/AiHuVD7ptyXhySkDEsE= | ||||||
| github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9/go.mod h1:A5SRAcpTemjGgIuBq6Kic2yHcoeUFWUinOAlMP/i9xo= | github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9/go.mod h1:A5SRAcpTemjGgIuBq6Kic2yHcoeUFWUinOAlMP/i9xo= | ||||||
| github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I= | github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I= | ||||||
| github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= | github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= | ||||||
| github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0= | github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0= | ||||||
| github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= | github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= | ||||||
|  | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= | ||||||
| github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= | 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/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 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= | ||||||
| github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= | github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= | ||||||
| github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83 h1:XQonH5Iv5rbyIkMJOQ4xKmKHQTh8viXtRSmep5Ca5I4= | github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw= | ||||||
| github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4= | 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= | github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcUhvI17yO9SlpAdDIJGkhDEgO8A= | ||||||
| github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= | github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= | ||||||
| github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= | ||||||
| github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | ||||||
| github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0= | github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0= | ||||||
| github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA= | github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA= | ||||||
| github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= |  | ||||||
| github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||||
|  | 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||||
|  | 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= | ||||||
|  | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||||
|  | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= | ||||||
|  | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | ||||||
|  | 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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||||||
| github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= | 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/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 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= | ||||||
| github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= | ||||||
| github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= |  | ||||||
| github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= | 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/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= | ||||||
| github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0= | 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/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI= | ||||||
| github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio= | github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio= | ||||||
| github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4= | github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4= | ||||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7 h1:80VN+vGkqM773Br/uNNTSheo3KatTgV8IpjIKjvVLng= | ||||||
| github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME= | github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||||||
| github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | 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/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE= | 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/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= | ||||||
| github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg= | github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg= | ||||||
| github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||||
| github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= | github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= | ||||||
| github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | ||||||
|  | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= | ||||||
|  | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | ||||||
| github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= | ||||||
| github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | ||||||
| github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= | ||||||
| @@ -157,25 +214,31 @@ 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/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 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= | ||||||
| github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | ||||||
| github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38= | github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= | ||||||
| github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
| github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
|  | github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= | ||||||
|  | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= | ||||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||||
| github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | ||||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||||
| github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= | github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= | ||||||
| github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= | github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= | ||||||
| github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | 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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||||||
| github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||||||
| github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= | github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= | ||||||
| github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= | ||||||
| github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= | 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/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/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-20190304061218-d34796e0a6c2 h1:UQwvu7FjUEdVYofx0U6bsc5odNE7wa5TSA0fl559GcA= | ||||||
| github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= | github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= | ||||||
| gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0= | gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0= | ||||||
| gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI= | gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI= | ||||||
| gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk= | gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk= | ||||||
| @@ -188,33 +251,68 @@ gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe h1:5kUPFAF5 | |||||||
| gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe/go.mod h1:P9LSM1KVzrIstFgUaveuwiAm8PK5VTB3yJEU8kqlbrU= | gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe/go.mod h1:P9LSM1KVzrIstFgUaveuwiAm8PK5VTB3yJEU8kqlbrU= | ||||||
| gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJWpU5qHKGNy///1pacZC9txiUI= | gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJWpU5qHKGNy///1pacZC9txiUI= | ||||||
| gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU= | gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU= | ||||||
| go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | ||||||
| go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | 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= | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= | ||||||
| go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= | ||||||
| go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= | ||||||
| go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= | ||||||
| golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||||
| golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||||
| golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |  | ||||||
| golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |  | ||||||
| golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||||
| golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= | ||||||
| golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 h1:+vH8qNweCrORN49012OX3h0oWEXO3p+rRnpAGQinddk= | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= | ||||||
| golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM= | golang.org/x/image v0.0.0-20190616094056-33659d3de4f5 h1:ngW7cqsJcNIFizl289rKwy+nVvw7TQS8z3ejrra6syo= | ||||||
| golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/image v0.0.0-20190616094056-33659d3de4f5/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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||||
|  | 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-20181114220301-adae6a3d119a/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-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/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||||
|  | golang.org/x/net v0.0.0-20190607181551-461777fb6f67 h1:rJJxsykSlULwd2P2+pg/rtnwN2FrWp4IuCxOSyS0V00= | ||||||
|  | golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||||
|  | 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 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= | ||||||
|  | golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
| golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190129075346-302c3dd5f1cc/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
| golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o= | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190609082536-301114b31cce h1:CQakrGkKbydnUmt7cFIlmQ4lNQiqdTPt6xzXij4nYCc= | ||||||
|  | golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/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-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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||||
|  | golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||||
|  | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||||
|  | 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/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||||
|  | google.golang.org/grpc v1.21.0/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 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | ||||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| @@ -222,11 +320,13 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | |||||||
| gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= | ||||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | ||||||
|  | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | ||||||
| gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | ||||||
| gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | 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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI= | rsc.io/goversion v1.0.0 h1:/IhXBiai89TyuerPquiZZ39IQkTfAUbZB2awsyYZ/2c= | ||||||
| maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI= | rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= | ||||||
| maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM= |  | ||||||
|   | |||||||
							
								
								
									
										288
									
								
								internal/bindata.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								internal/bindata.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | // Code generated by go-bindata. DO NOT EDIT. | ||||||
|  | // sources: | ||||||
|  | // tengo/outmessage.tengo | ||||||
|  |  | ||||||
|  | package internal | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"compress/gzip" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func bindataRead(data []byte, name string) ([]byte, error) { | ||||||
|  | 	gz, err := gzip.NewReader(bytes.NewBuffer(data)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Read %q: %v", name, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	_, err = io.Copy(&buf, gz) | ||||||
|  | 	clErr := gz.Close() | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Read %q: %v", name, err) | ||||||
|  | 	} | ||||||
|  | 	if clErr != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf.Bytes(), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | type asset struct { | ||||||
|  | 	bytes []byte | ||||||
|  | 	info  fileInfoEx | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type fileInfoEx interface { | ||||||
|  | 	os.FileInfo | ||||||
|  | 	MD5Checksum() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type bindataFileInfo struct { | ||||||
|  | 	name        string | ||||||
|  | 	size        int64 | ||||||
|  | 	mode        os.FileMode | ||||||
|  | 	modTime     time.Time | ||||||
|  | 	md5checksum string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fi bindataFileInfo) Name() string { | ||||||
|  | 	return fi.name | ||||||
|  | } | ||||||
|  | func (fi bindataFileInfo) Size() int64 { | ||||||
|  | 	return fi.size | ||||||
|  | } | ||||||
|  | func (fi bindataFileInfo) Mode() os.FileMode { | ||||||
|  | 	return fi.mode | ||||||
|  | } | ||||||
|  | func (fi bindataFileInfo) ModTime() time.Time { | ||||||
|  | 	return fi.modTime | ||||||
|  | } | ||||||
|  | func (fi bindataFileInfo) MD5Checksum() string { | ||||||
|  | 	return fi.md5checksum | ||||||
|  | } | ||||||
|  | func (fi bindataFileInfo) IsDir() bool { | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | func (fi bindataFileInfo) Sys() interface{} { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _bindataTengoOutmessagetengo = []byte( | ||||||
|  | 	"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x91\x3d\x8f\xda\x40\x10\x86\xfb\xfd\x15\x13\x37\xb1\x2d\x07\xe7\xa3" + | ||||||
|  | 	"\xb3\x64\x59\x11\x45\x94\x2e\x8a\x92\x0a\xd0\xb1\xac\x07\x33\xd2\x7a\xc7\x1a\x8f\x31\x88\xe3\xbf\x9f\xcc\x01\x47" + | ||||||
|  | 	"\x7f\xc5\x75\xef\xae\x9e\x9d\x77\x1f\x4d\x9e\x9a\xbd\x15\xb2\x1b\x8f\x3d\xd8\xbd\x25\x3f\x45\x30\x82\xb6\xfe\xc2" + | ||||||
|  | 	"\xc1\x1f\x0b\x43\xe1\xa7\x73\x3c\x04\xcd\x80\xc2\x1f\x61\x65\xc7\x7e\xca\xf3\x9d\x0d\x01\x2f\xf1\x97\x55\x1c\xed" + | ||||||
|  | 	"\xd1\xf0\xa0\x77\x98\x07\x7d\xa3\x79\xd0\x3b\xce\x83\xde\xf8\xd7\x9e\x51\x48\xb1\x30\x6d\xdf\xfc\xc3\x83\x66\xd0" + | ||||||
|  | 	"\xf6\xcd\xff\x1e\x25\xd8\x16\x4d\x9a\x1b\xa3\x78\x50\x28\x4a\xa0\xb6\x63\xd1\x38\x9a\xce\x51\x62\x4c\x9e\x43\xaf" + | ||||||
|  | 	"\x42\x1d\x90\x38\x70\xec\x59\xfa\xe9\x8e\xb6\x30\xe2\x67\x41\x08\xac\xd0\x63\xa8\x29\x34\xa0\x0c\x36\x5c\xc0\x8d" + | ||||||
|  | 	"\x50\xdd\x20\x8c\x78\x7d\xac\x3b\x84\xdf\x7f\xe7\xb7\x01\xb4\x7d\xd0\x84\xb2\x84\x88\xc4\x45\x70\x32\x00\x00\x82" + | ||||||
|  | 	"\xd3\x3f\xa6\xfe\x99\xe0\x93\xe3\xb6\x23\x8f\xf1\x7a\x79\xf8\xfa\x23\xae\x8a\x65\x7d\xfa\x96\x7d\x3f\xc7\x55\x91" + | ||||||
|  | 	"\x5d\x63\x52\x25\xd5\xf3\x62\x51\xb8\xa0\xe2\x8b\xd5\x6a\x9d\x5c\xc6\x5c\x4d\x4b\xc1\x99\x60\xe7\xad\xc3\xf8\x26" + | ||||||
|  | 	"\x1f\x45\x89\x39\x9b\xf7\x6b\xe4\x29\x6d\x1f\x57\x00\x9f\x3e\xc6\x24\xcd\xcd\x4b\x00\x00\x00\xff\xff\x40\xb8\x54" + | ||||||
|  | 	"\xb8\x64\x02\x00\x00") | ||||||
|  |  | ||||||
|  | func bindataTengoOutmessagetengoBytes() ([]byte, error) { | ||||||
|  | 	return bindataRead( | ||||||
|  | 		_bindataTengoOutmessagetengo, | ||||||
|  | 		"tengo/outmessage.tengo", | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func bindataTengoOutmessagetengo() (*asset, error) { | ||||||
|  | 	bytes, err := bindataTengoOutmessagetengoBytes() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	info := bindataFileInfo{ | ||||||
|  | 		name: "tengo/outmessage.tengo", | ||||||
|  | 		size: 612, | ||||||
|  | 		md5checksum: "", | ||||||
|  | 		mode: os.FileMode(420), | ||||||
|  | 		modTime: time.Unix(1555622139, 0), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	a := &asset{bytes: bytes, info: info} | ||||||
|  |  | ||||||
|  | 	return a, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // Asset loads and returns the asset for the given name. | ||||||
|  | // It returns an error if the asset could not be found or | ||||||
|  | // could not be loaded. | ||||||
|  | // | ||||||
|  | func Asset(name string) ([]byte, error) { | ||||||
|  | 	cannonicalName := strings.Replace(name, "\\", "/", -1) | ||||||
|  | 	if f, ok := _bindata[cannonicalName]; ok { | ||||||
|  | 		a, err := f() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) | ||||||
|  | 		} | ||||||
|  | 		return a.bytes, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // MustAsset is like Asset but panics when Asset would return an error. | ||||||
|  | // It simplifies safe initialization of global variables. | ||||||
|  | // nolint: deadcode | ||||||
|  | // | ||||||
|  | func MustAsset(name string) []byte { | ||||||
|  | 	a, err := Asset(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic("asset: Asset(" + name + "): " + err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // AssetInfo loads and returns the asset info for the given name. | ||||||
|  | // It returns an error if the asset could not be found or could not be loaded. | ||||||
|  | // | ||||||
|  | func AssetInfo(name string) (os.FileInfo, error) { | ||||||
|  | 	cannonicalName := strings.Replace(name, "\\", "/", -1) | ||||||
|  | 	if f, ok := _bindata[cannonicalName]; ok { | ||||||
|  | 		a, err := f() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) | ||||||
|  | 		} | ||||||
|  | 		return a.info, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // AssetNames returns the names of the assets. | ||||||
|  | // nolint: deadcode | ||||||
|  | // | ||||||
|  | func AssetNames() []string { | ||||||
|  | 	names := make([]string, 0, len(_bindata)) | ||||||
|  | 	for name := range _bindata { | ||||||
|  | 		names = append(names, name) | ||||||
|  | 	} | ||||||
|  | 	return names | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // _bindata is a table, holding each asset generator, mapped to its name. | ||||||
|  | // | ||||||
|  | var _bindata = map[string]func() (*asset, error){ | ||||||
|  | 	"tengo/outmessage.tengo": bindataTengoOutmessagetengo, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // AssetDir returns the file names below a certain | ||||||
|  | // directory embedded in the file by go-bindata. | ||||||
|  | // For example if you run go-bindata on data/... and data contains the | ||||||
|  | // following hierarchy: | ||||||
|  | //     data/ | ||||||
|  | //       foo.txt | ||||||
|  | //       img/ | ||||||
|  | //         a.png | ||||||
|  | //         b.png | ||||||
|  | // then AssetDir("data") would return []string{"foo.txt", "img"} | ||||||
|  | // AssetDir("data/img") would return []string{"a.png", "b.png"} | ||||||
|  | // AssetDir("foo.txt") and AssetDir("notexist") would return an error | ||||||
|  | // AssetDir("") will return []string{"data"}. | ||||||
|  | // | ||||||
|  | func AssetDir(name string) ([]string, error) { | ||||||
|  | 	node := _bintree | ||||||
|  | 	if len(name) != 0 { | ||||||
|  | 		cannonicalName := strings.Replace(name, "\\", "/", -1) | ||||||
|  | 		pathList := strings.Split(cannonicalName, "/") | ||||||
|  | 		for _, p := range pathList { | ||||||
|  | 			node = node.Children[p] | ||||||
|  | 			if node == nil { | ||||||
|  | 				return nil, &os.PathError{ | ||||||
|  | 					Op: "open", | ||||||
|  | 					Path: name, | ||||||
|  | 					Err: os.ErrNotExist, | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if node.Func != nil { | ||||||
|  | 		return nil, &os.PathError{ | ||||||
|  | 			Op: "open", | ||||||
|  | 			Path: name, | ||||||
|  | 			Err: os.ErrNotExist, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	rv := make([]string, 0, len(node.Children)) | ||||||
|  | 	for childName := range node.Children { | ||||||
|  | 		rv = append(rv, childName) | ||||||
|  | 	} | ||||||
|  | 	return rv, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | type bintree struct { | ||||||
|  | 	Func     func() (*asset, error) | ||||||
|  | 	Children map[string]*bintree | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _bintree = &bintree{Func: nil, Children: map[string]*bintree{ | ||||||
|  | 	"tengo": {Func: nil, Children: map[string]*bintree{ | ||||||
|  | 		"outmessage.tengo": {Func: bindataTengoOutmessagetengo, Children: map[string]*bintree{}}, | ||||||
|  | 	}}, | ||||||
|  | }} | ||||||
|  |  | ||||||
|  | // RestoreAsset restores an asset under the given directory | ||||||
|  | func RestoreAsset(dir, name string) error { | ||||||
|  | 	data, err := Asset(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	info, err := AssetInfo(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RestoreAssets restores an asset under the given directory recursively | ||||||
|  | func RestoreAssets(dir, name string) error { | ||||||
|  | 	children, err := AssetDir(name) | ||||||
|  | 	// File | ||||||
|  | 	if err != nil { | ||||||
|  | 		return RestoreAsset(dir, name) | ||||||
|  | 	} | ||||||
|  | 	// Dir | ||||||
|  | 	for _, child := range children { | ||||||
|  | 		err = RestoreAssets(dir, filepath.Join(name, child)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func _filePath(dir, name string) string { | ||||||
|  | 	cannonicalName := strings.Replace(name, "\\", "/", -1) | ||||||
|  | 	return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								internal/tengo/outmessage.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								internal/tengo/outmessage.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | /* | ||||||
|  | variables available  | ||||||
|  | read-only: | ||||||
|  | inAccount, inProtocol, inChannel, inGateway | ||||||
|  | outAccount, outProtocol, outChannel, outGateway | ||||||
|  |  | ||||||
|  | read-write: | ||||||
|  | msgText, msgUsername | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | text := import("text") | ||||||
|  |  | ||||||
|  | // start - strip irc colors  | ||||||
|  | // if we're not sending to an irc bridge we strip the IRC colors | ||||||
|  | if inProtocol == "irc" { | ||||||
|  |     re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`) | ||||||
|  |     msgText=re.replace(msgText,"") | ||||||
|  | } | ||||||
|  | // end - strip irc colors | ||||||
| @@ -15,7 +15,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	version = "1.14.1" | 	version = "1.15.1" | ||||||
| 	githash string | 	githash string | ||||||
|  |  | ||||||
| 	flagConfig  = flag.String("conf", "matterbridge.toml", "config file") | 	flagConfig  = flag.String("conf", "matterbridge.toml", "config file") | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| #This is configuration for matterbridge. | #This is configuration for matterbridge. | ||||||
| #WARNING: as this file contains credentials, be sure to set correct file permissions | #WARNING: as this file contains credentials, be sure to set correct file permissions | ||||||
|  | #See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for how to create your config | ||||||
|  | #See https://github.com/42wim/matterbridge/wiki/Settings for all settings | ||||||
| ################################################################### | ################################################################### | ||||||
| #IRC section | #IRC section | ||||||
| ################################################################### | ################################################################### | ||||||
| @@ -27,7 +29,7 @@ UseTLS=false | |||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| UseSASL=false | UseSASL=false | ||||||
|  |  | ||||||
| #Enable to not verify the certificate on your irc server. i | #Enable to not verify the certificate on your irc server. | ||||||
| #e.g. when using selfsigned certificates | #e.g. when using selfsigned certificates | ||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| SkipTLSVerify=true | SkipTLSVerify=true | ||||||
| @@ -155,6 +157,11 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | |||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| ShowJoinPart=false | ShowJoinPart=false | ||||||
|  |  | ||||||
|  | #Enable to show verbose users joins/parts (ident@host) from other bridges | ||||||
|  | #Currently works for messages from the following bridges: irc | ||||||
|  | #OPTIONAL (default false) | ||||||
|  | VerboseJoinPart=false | ||||||
|  |  | ||||||
| #Do not send joins/parts to other bridges | #Do not send joins/parts to other bridges | ||||||
| #Currently works for messages from the following bridges: irc, mattermost, slack | #Currently works for messages from the following bridges: irc, mattermost, slack | ||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| @@ -270,98 +277,6 @@ StripNick=false | |||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| ShowTopicChange=false | ShowTopicChange=false | ||||||
|  |  | ||||||
| ################################################################### |  | ||||||
| #hipchat section |  | ||||||
| ################################################################### |  | ||||||
| #Go to https://www.hipchat.com/account/xmpp this will show you the necessary data |  | ||||||
| #to fill in the section below |  | ||||||
| [xmpp.hipchat] |  | ||||||
| #xmpp server to connect to.  |  | ||||||
| #REQUIRED |  | ||||||
| Server="chat.hipchat.com:5222" |  | ||||||
|  |  | ||||||
| #Jabber ID |  | ||||||
| #REQUIRED |  | ||||||
| Jid="12345_12345@chat.hipchat.com" |  | ||||||
|  |  | ||||||
| #Password (your hipchat password) |  | ||||||
| #REQUIRED |  | ||||||
| Password="yourpass" |  | ||||||
|  |  | ||||||
| #Conference (MUC) domain |  | ||||||
| #REQUIRED |  | ||||||
| Muc="conf.hipchat.com" |  | ||||||
|  |  | ||||||
| #Room nickname |  | ||||||
| #REQUIRED |  | ||||||
| Nick="yourlogin" |  | ||||||
|  |  | ||||||
| ## RELOADABLE SETTINGS |  | ||||||
| ## Settings below can be reloaded by editing the file |  | ||||||
|  |  | ||||||
| #Nicks you want to ignore.  |  | ||||||
| #Regular expressions supported |  | ||||||
| #Messages from those users will not be sent to other bridges. |  | ||||||
| #OPTIONAL |  | ||||||
| IgnoreNicks="spammer1 spammer2" |  | ||||||
|  |  | ||||||
| #Messages you want to ignore.  |  | ||||||
| #Messages matching these regexp will be ignored and not sent to other bridges |  | ||||||
| #See https://regex-golang.appspot.com/assets/html/index.html for more regex info |  | ||||||
| #OPTIONAL (example below ignores messages starting with ~~ or messages containing badword |  | ||||||
| IgnoreMessages="^~~ badword" |  | ||||||
|  |  | ||||||
| #messages you want to replace. |  | ||||||
| #it replaces outgoing messages from the bridge. |  | ||||||
| #so you need to place it by the sending bridge definition. |  | ||||||
| #regular expressions supported |  | ||||||
| #some examples: |  | ||||||
| #this replaces cat => dog and sleep => awake |  | ||||||
| #replacemessages=[ ["cat","dog"], ["sleep","awake"] ] |  | ||||||
| #this replaces every number with number.  123 => numbernumbernumber |  | ||||||
| #replacemessages=[ ["[0-9]","number"] ] |  | ||||||
| #optional (default empty) |  | ||||||
| ReplaceMessages=[ ["cat","dog"] ] |  | ||||||
|  |  | ||||||
| #nicks you want to replace. |  | ||||||
| #see replacemessages for syntaxa |  | ||||||
| #optional (default empty) |  | ||||||
| ReplaceNicks=[ ["user--","user"] ] |  | ||||||
|  |  | ||||||
| #Extractnicks is used to for example rewrite messages from other relaybots |  | ||||||
| #See https://github.com/42wim/matterbridge/issues/713 and https://github.com/42wim/matterbridge/issues/466 |  | ||||||
| #some examples: |  | ||||||
| #this replaces a message like "Relaybot: <relayeduser> something interesting" to "relayeduser: something interesting" |  | ||||||
| #ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ] |  | ||||||
| #you can use multiple entries for multiplebots |  | ||||||
| #this also replaces a message like "otherbot: (relayeduser) something else" to "relayeduser: something else" |  | ||||||
| #ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ] |  | ||||||
| #OPTIONAL (default empty) |  | ||||||
| ExtractNicks=[ ["otherbot","<(.*?)>\\s+" ] ] |  | ||||||
|  |  | ||||||
| #extra label that can be used in the RemoteNickFormat |  | ||||||
| #optional (default empty) |  | ||||||
| Label="" |  | ||||||
|  |  | ||||||
| #RemoteNickFormat defines how remote users appear on this bridge  |  | ||||||
| #See [general] config section for default options |  | ||||||
| RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> " |  | ||||||
|  |  | ||||||
| #Enable to show users joins/parts from other bridges  |  | ||||||
| #Currently works for messages from the following bridges: irc, mattermost, slack, discord |  | ||||||
| #OPTIONAL (default false) |  | ||||||
| ShowJoinPart=false |  | ||||||
|  |  | ||||||
| #StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285 |  | ||||||
| #It will strip other characters from the nick |  | ||||||
| #OPTIONAL (default false) |  | ||||||
| StripNick=false |  | ||||||
|  |  | ||||||
| #Enable to show topic changes from other bridges  |  | ||||||
| #Only works hiding/show topic changes from slack bridge for now |  | ||||||
| #OPTIONAL (default false) |  | ||||||
| ShowTopicChange=false |  | ||||||
|  |  | ||||||
| ################################################################### | ################################################################### | ||||||
| #mattermost section | #mattermost section | ||||||
| ################################################################### | ################################################################### | ||||||
| @@ -435,6 +350,12 @@ NickFormatter="plain" | |||||||
| #OPTIONAL (default 4) | #OPTIONAL (default 4) | ||||||
| NicksPerRow=4 | NicksPerRow=4 | ||||||
|  |  | ||||||
|  | #Skip the Mattermost server version checks that are normally done when connecting. | ||||||
|  | #The usage scenario for this feature would be when the Mattermost instance is hosted behind a | ||||||
|  | #reverse proxy that suppresses "non-standard" response headers in flight. | ||||||
|  | #OPTIONAL (default false) | ||||||
|  | SkipVersionCheck=false | ||||||
|  |  | ||||||
| #Whether to prefix messages from other bridges to mattermost with the sender's nick.  | #Whether to prefix messages from other bridges to mattermost with the sender's nick.  | ||||||
| #Useful if username overrides for incoming webhooks isn't enabled on the  | #Useful if username overrides for incoming webhooks isn't enabled on the  | ||||||
| #mattermost server. If you set PrefixMessagesWithNick to true, each message  | #mattermost server. If you set PrefixMessagesWithNick to true, each message  | ||||||
| @@ -1480,6 +1401,7 @@ RemoteNickFormat="{NICK}" | |||||||
| #The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge | #The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge | ||||||
| #The string "{GATEWAY}" (case sensitive) will be replaced by the origin gateway name that is replicating the message. | #The string "{GATEWAY}" (case sensitive) will be replaced by the origin gateway name that is replicating the message. | ||||||
| #The string "{CHANNEL}" (case sensitive) will be replaced by the origin channel name used by the bridge | #The string "{CHANNEL}" (case sensitive) will be replaced by the origin channel name used by the bridge | ||||||
|  | #The string "{TENGO}" (case sensitive) will be replaced by the output of the RemoteNickFormat script under [tengo] | ||||||
| #OPTIONAL (default empty) | #OPTIONAL (default empty) | ||||||
| RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | RemoteNickFormat="[{PROTOCOL}] <{NICK}> " | ||||||
|  |  | ||||||
| @@ -1529,8 +1451,14 @@ MediaDownloadBlacklist=[".html$",".htm$"] | |||||||
| #OPTIONAL (default false) | #OPTIONAL (default false) | ||||||
| IgnoreFailureOnStart=false | IgnoreFailureOnStart=false | ||||||
|  |  | ||||||
|  | ################################################################### | ||||||
|  | #Tengo configuration | ||||||
|  | ################################################################### | ||||||
|  | #More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and | ||||||
|  | #https://github.com/d5/tengo/blob/master/docs/stdlib.md | ||||||
|  |  | ||||||
| #TengoModifyMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script. | [tengo] | ||||||
|  | #InMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script. | ||||||
| #This script will receive every incoming message and can be used to modify the Username and the Text of that message. | #This script will receive every incoming message and can be used to modify the Username and the Text of that message. | ||||||
| #The script will have the following global variables: | #The script will have the following global variables: | ||||||
| #to modify: msgUsername and msgText | #to modify: msgUsername and msgText | ||||||
| @@ -1547,10 +1475,42 @@ IgnoreFailureOnStart=false | |||||||
| #    msgText="replaced by this" | #    msgText="replaced by this" | ||||||
| #    msgUsername="fakeuser" | #    msgUsername="fakeuser" | ||||||
| #} | #} | ||||||
| #More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and |  | ||||||
| #https://github.com/d5/tengo/blob/master/docs/stdlib.md |  | ||||||
| #OPTIONAL (default empty) | #OPTIONAL (default empty) | ||||||
| TengoModifyMessage="example.tengo" | InMessage="example.tengo" | ||||||
|  |  | ||||||
|  | #OutMessage allows you to specify the location of the script that | ||||||
|  | #will be invoked on each message being sent to a bridge and can be used to modify the Username | ||||||
|  | #and the Text of that message. | ||||||
|  | # | ||||||
|  | #The script will have the following global variables: | ||||||
|  | #read-only: | ||||||
|  | #inAccount, inProtocol, inChannel, inGateway, inEvent | ||||||
|  | #outAccount, outProtocol, outChannel, outGateway, outEvent | ||||||
|  | # | ||||||
|  | #read-write: | ||||||
|  | #msgText, msgUsername | ||||||
|  | # | ||||||
|  | #The script is reloaded on every message, so you can modify the script on the fly. | ||||||
|  | # | ||||||
|  | #The default script in https://github.com/42wim/matterbridge/tree/master/internal/tengo/outmessage.tengo | ||||||
|  | #is compiled in and will be executed if no script is specified. | ||||||
|  | #OPTIONAL (default empty) | ||||||
|  | OutMessage="example.tengo" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #RemoteNickFormat allows you to specify the location of a tengo (https://github.com/d5/tengo/) script. | ||||||
|  | #The script will have the following global variables: | ||||||
|  | #to modify: result | ||||||
|  | #to read: channel, bridge, gateway, protocol, nick | ||||||
|  | # | ||||||
|  | #The result will be set in {TENGO} in the RemoteNickFormat key of every bridge where {TENGO} is specified | ||||||
|  | # | ||||||
|  | #The script is reloaded on every message, so you can modify the script on the fly. | ||||||
|  | # | ||||||
|  | #Example script can be found in https://github.com/42wim/matterbridge/tree/master/contrib/remotenickformat.tengo | ||||||
|  | # | ||||||
|  | #OPTIONAL (default empty) | ||||||
|  | RemoteNickFormat="remotenickformat.tengo" | ||||||
|  |  | ||||||
| ################################################################### | ################################################################### | ||||||
| #Gateway configuration | #Gateway configuration | ||||||
| @@ -1593,6 +1553,7 @@ enable=true | |||||||
|     # discord    - channel (without the #) |     # discord    - channel (without the #) | ||||||
|     #            - ID:123456789 (where 123456789 is the channel ID) |     #            - ID:123456789 (where 123456789 is the channel ID) | ||||||
|     #               (https://github.com/42wim/matterbridge/issues/57) |     #               (https://github.com/42wim/matterbridge/issues/57) | ||||||
|  |     #            - category/channel (without the #) if you're using discord categories to group your channels | ||||||
|     # telegram   - chatid (a large negative number, eg -123456789) |     # telegram   - chatid (a large negative number, eg -123456789) | ||||||
|     #             see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau) |     #             see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau) | ||||||
|     # hipchat    - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel) |     # hipchat    - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel) | ||||||
|   | |||||||
| @@ -51,8 +51,9 @@ func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:go | |||||||
| 				if res == name { | 				if res == name { | ||||||
| 					return channel.Id | 					return channel.Id | ||||||
| 				} | 				} | ||||||
|  | 			} else if channel.Name == name { | ||||||
|  | 				return channel.Id | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| @@ -166,23 +167,42 @@ func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (m *MMClient) UpdateChannelsTeam(teamID string) error { | ||||||
|  | 	mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "") | ||||||
|  | 	if resp.Error != nil { | ||||||
|  | 		return errors.New(resp.Error.DetailedError) | ||||||
|  | 	} | ||||||
|  | 	for idx, t := range m.OtherTeams { | ||||||
|  | 		if t.Id == teamID { | ||||||
|  | 			m.Lock() | ||||||
|  | 			m.OtherTeams[idx].Channels = mmchannels | ||||||
|  | 			m.Unlock() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mmchannels, resp = m.Client.GetPublicChannelsForTeam(teamID, 0, 5000, "") | ||||||
|  | 	if resp.Error != nil { | ||||||
|  | 		return errors.New(resp.Error.DetailedError) | ||||||
|  | 	} | ||||||
|  | 	for idx, t := range m.OtherTeams { | ||||||
|  | 		if t.Id == teamID { | ||||||
|  | 			m.Lock() | ||||||
|  | 			m.OtherTeams[idx].MoreChannels = mmchannels | ||||||
|  | 			m.Unlock() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func (m *MMClient) UpdateChannels() error { | func (m *MMClient) UpdateChannels() error { | ||||||
| 	mmchannels, resp := m.Client.GetChannelsForTeamForUser(m.Team.Id, m.User.Id, "") | 	if err := m.UpdateChannelsTeam(m.Team.Id); err != nil { | ||||||
| 	if resp.Error != nil { | 		return err | ||||||
| 		return errors.New(resp.Error.DetailedError) |  | ||||||
| 	} | 	} | ||||||
| 	m.Lock() | 	for _, t := range m.OtherTeams { | ||||||
| 	m.Team.Channels = mmchannels | 		if err := m.UpdateChannelsTeam(t.Id); err != nil { | ||||||
| 	m.Unlock() | 			return err | ||||||
|  | 		} | ||||||
| 	mmchannels, resp = m.Client.GetPublicChannelsForTeam(m.Team.Id, 0, 5000, "") |  | ||||||
| 	if resp.Error != nil { |  | ||||||
| 		return errors.New(resp.Error.DetailedError) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	m.Lock() |  | ||||||
| 	m.Team.MoreChannels = mmchannels |  | ||||||
| 	m.Unlock() |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -132,14 +132,25 @@ func (m *MMClient) initUser() error { | |||||||
| 		return resp.Error | 		return resp.Error | ||||||
| 	} | 	} | ||||||
| 	for _, team := range teams { | 	for _, team := range teams { | ||||||
| 		mmusers, resp := m.Client.GetUsersInTeam(team.Id, 0, 50000, "") | 		idx := 0 | ||||||
|  | 		max := 200 | ||||||
|  | 		usermap := make(map[string]*model.User) | ||||||
|  | 		mmusers, resp := m.Client.GetUsersInTeam(team.Id, idx, max, "") | ||||||
| 		if resp.Error != nil { | 		if resp.Error != nil { | ||||||
| 			return errors.New(resp.Error.DetailedError) | 			return errors.New(resp.Error.DetailedError) | ||||||
| 		} | 		} | ||||||
| 		usermap := make(map[string]*model.User) | 		for len(mmusers) > 0 { | ||||||
| 		for _, user := range mmusers { | 			for _, user := range mmusers { | ||||||
| 			usermap[user.Id] = user | 				usermap[user.Id] = user | ||||||
|  | 			} | ||||||
|  | 			mmusers, resp = m.Client.GetUsersInTeam(team.Id, idx, max, "") | ||||||
|  | 			if resp.Error != nil { | ||||||
|  | 				return errors.New(resp.Error.DetailedError) | ||||||
|  | 			} | ||||||
|  | 			idx++ | ||||||
|  | 			time.Sleep(time.Millisecond * 200) | ||||||
| 		} | 		} | ||||||
|  | 		m.logger.Infof("found %d users in team %s", len(usermap), team.Name) | ||||||
|  |  | ||||||
| 		t := &Team{Team: team, Users: usermap, Id: team.Id} | 		t := &Team{Team: team, Users: usermap, Id: team.Id} | ||||||
|  |  | ||||||
| @@ -175,15 +186,19 @@ func (m *MMClient) serverAlive(firstConnection bool, b *backoff.Backoff) error { | |||||||
| 		if resp.Error != nil { | 		if resp.Error != nil { | ||||||
| 			return fmt.Errorf("%#v", resp.Error.Error()) | 			return fmt.Errorf("%#v", resp.Error.Error()) | ||||||
| 		} | 		} | ||||||
| 		if firstConnection && !supportedVersion(resp.ServerVersion) { | 		if firstConnection && !m.SkipVersionCheck && !supportedVersion(resp.ServerVersion) { | ||||||
| 			return fmt.Errorf("unsupported mattermost version: %s", resp.ServerVersion) | 			return fmt.Errorf("unsupported mattermost version: %s", resp.ServerVersion) | ||||||
| 		} | 		} | ||||||
| 		m.ServerVersion = resp.ServerVersion | 		if !m.SkipVersionCheck { | ||||||
| 		if m.ServerVersion == "" { | 			m.ServerVersion = resp.ServerVersion | ||||||
| 			m.logger.Debugf("Server not up yet, reconnecting in %s", d) | 			if m.ServerVersion == "" { | ||||||
| 			time.Sleep(d) | 				m.logger.Debugf("Server not up yet, reconnecting in %s", d) | ||||||
|  | 				time.Sleep(d) | ||||||
|  | 			} else { | ||||||
|  | 				m.logger.Infof("Found version %s", m.ServerVersion) | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			m.logger.Infof("Found version %s", m.ServerVersion) |  | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -16,14 +16,15 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type Credentials struct { | type Credentials struct { | ||||||
| 	Login         string | 	Login            string | ||||||
| 	Team          string | 	Team             string | ||||||
| 	Pass          string | 	Pass             string | ||||||
| 	Token         string | 	Token            string | ||||||
| 	CookieToken   bool | 	CookieToken      bool | ||||||
| 	Server        string | 	Server           string | ||||||
| 	NoTLS         bool | 	NoTLS            bool | ||||||
| 	SkipTLSVerify bool | 	SkipTLSVerify    bool | ||||||
|  | 	SkipVersionCheck bool | ||||||
| } | } | ||||||
|  |  | ||||||
| type Message struct { | type Message struct { | ||||||
| @@ -216,9 +217,17 @@ func (m *MMClient) WsReceiver() { | |||||||
| 			if msg.Post != nil { | 			if msg.Post != nil { | ||||||
| 				if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" { | 				if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" { | ||||||
| 					m.MessageChan <- msg | 					m.MessageChan <- msg | ||||||
|  | 					continue | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			continue | 			switch msg.Raw.Event { | ||||||
|  | 			case model.WEBSOCKET_EVENT_USER_ADDED, | ||||||
|  | 				model.WEBSOCKET_EVENT_USER_REMOVED, | ||||||
|  | 				model.WEBSOCKET_EVENT_CHANNEL_CREATED, | ||||||
|  | 				model.WEBSOCKET_EVENT_CHANNEL_DELETED: | ||||||
|  | 				m.MessageChan <- msg | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		var response model.WebSocketResponse | 		var response model.WebSocketResponse | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ func (m *MMClient) DeleteMessage(postId string) error { //nolint:golint | |||||||
| } | } | ||||||
|  |  | ||||||
| func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint | func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint | ||||||
| 	post := &model.Post{Message: text} | 	post := &model.Post{Message: text, Id: postId} | ||||||
| 	res, resp := m.Client.UpdatePost(postId, post) | 	res, resp := m.Client.UpdatePost(postId, post) | ||||||
| 	if resp.Error != nil { | 	if resp.Error != nil { | ||||||
| 		return "", resp.Error | 		return "", resp.Error | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package matterclient | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/mattermost/mattermost-server/model" | 	"github.com/mattermost/mattermost-server/model" | ||||||
| ) | ) | ||||||
| @@ -99,15 +100,25 @@ func (m *MMClient) GetUsers() map[string]*model.User { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (m *MMClient) UpdateUsers() error { | func (m *MMClient) UpdateUsers() error { | ||||||
| 	mmusers, resp := m.Client.GetUsers(0, 50000, "") | 	idx := 0 | ||||||
|  | 	max := 200 | ||||||
|  | 	mmusers, resp := m.Client.GetUsers(idx, max, "") | ||||||
| 	if resp.Error != nil { | 	if resp.Error != nil { | ||||||
| 		return errors.New(resp.Error.DetailedError) | 		return errors.New(resp.Error.DetailedError) | ||||||
| 	} | 	} | ||||||
| 	m.Lock() | 	for len(mmusers) > 0 { | ||||||
| 	for _, user := range mmusers { | 		m.Lock() | ||||||
| 		m.Users[user.Id] = user | 		for _, user := range mmusers { | ||||||
|  | 			m.Users[user.Id] = user | ||||||
|  | 		} | ||||||
|  | 		m.Unlock() | ||||||
|  | 		mmusers, resp = m.Client.GetUsers(idx, max, "") | ||||||
|  | 		time.Sleep(time.Millisecond * 300) | ||||||
|  | 		if resp.Error != nil { | ||||||
|  | 			return errors.New(resp.Error.DetailedError) | ||||||
|  | 		} | ||||||
|  | 		idx++ | ||||||
| 	} | 	} | ||||||
| 	m.Unlock() |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,2 +1,3 @@ | |||||||
| .idea/ | .idea/ | ||||||
| docs/ | docs/ | ||||||
|  | build/ | ||||||
| @@ -3,7 +3,7 @@ Package rhymen/go-whatsapp implements the WhatsApp Web API to provide a clean in | |||||||
| 
 | 
 | ||||||
| ## Installation | ## Installation | ||||||
| ```sh | ```sh | ||||||
| go get github.com/rhymen/go-whatsapp | go get github.com/Rhymen/go-whatsapp | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Usage | ## Usage | ||||||
| @@ -30,7 +30,7 @@ The authentication process requires you to scan the qr code, that is send throug | |||||||
| 
 | 
 | ||||||
| ### Restore | ### Restore | ||||||
| ```go | ```go | ||||||
| newSess, err := wac.RestoreSession(sess) | newSess, err := wac.RestoreWithSession(sess) | ||||||
| ``` | ``` | ||||||
| The restore function needs a valid session and returns the new session that was created. | The restore function needs a valid session and returns the new session that was created. | ||||||
| 
 | 
 | ||||||
| @@ -2,7 +2,7 @@ package binary | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary/token" | 	"github.com/Rhymen/go-whatsapp/binary/token" | ||||||
| 	"io" | 	"io" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| ) | ) | ||||||
| @@ -2,7 +2,7 @@ package binary | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary/token" | 	"github.com/Rhymen/go-whatsapp/binary/token" | ||||||
| 	"math" | 	"math" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -2,7 +2,7 @@ package binary | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	pb "github.com/matterbridge/go-whatsapp/binary/proto" | 	pb "github.com/Rhymen/go-whatsapp/binary/proto" | ||||||
| 	"github.com/golang/protobuf/proto" | 	"github.com/golang/protobuf/proto" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
							
								
								
									
										210
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | //Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers. | ||||||
|  | package whatsapp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/rand" | ||||||
|  | 	"net/http" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type metric byte | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	debugLog metric = iota + 1 | ||||||
|  | 	queryResume | ||||||
|  | 	queryReceipt | ||||||
|  | 	queryMedia | ||||||
|  | 	queryChat | ||||||
|  | 	queryContacts | ||||||
|  | 	queryMessages | ||||||
|  | 	presence | ||||||
|  | 	presenceSubscribe | ||||||
|  | 	group | ||||||
|  | 	read | ||||||
|  | 	chat | ||||||
|  | 	received | ||||||
|  | 	pic | ||||||
|  | 	status | ||||||
|  | 	message | ||||||
|  | 	queryActions | ||||||
|  | 	block | ||||||
|  | 	queryGroup | ||||||
|  | 	queryPreview | ||||||
|  | 	queryEmoji | ||||||
|  | 	queryMessageInfo | ||||||
|  | 	spam | ||||||
|  | 	querySearch | ||||||
|  | 	queryIdentity | ||||||
|  | 	queryUrl | ||||||
|  | 	profile | ||||||
|  | 	contact | ||||||
|  | 	queryVcard | ||||||
|  | 	queryStatus | ||||||
|  | 	queryStatusUpdate | ||||||
|  | 	privacyStatus | ||||||
|  | 	queryLiveLocations | ||||||
|  | 	liveLocation | ||||||
|  | 	queryVname | ||||||
|  | 	queryLabels | ||||||
|  | 	call | ||||||
|  | 	queryCall | ||||||
|  | 	queryQuickReplies | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type flag byte | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	ignore flag = 1 << (7 - iota) | ||||||
|  | 	ackRequest | ||||||
|  | 	available | ||||||
|  | 	notAvailable | ||||||
|  | 	expires | ||||||
|  | 	skipOffline | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package. | ||||||
|  | It holds all necessary information to make the package work internally. | ||||||
|  | */ | ||||||
|  | type Conn struct { | ||||||
|  | 	ws       *websocketWrapper | ||||||
|  | 	listener *listenerWrapper | ||||||
|  |  | ||||||
|  | 	connected bool | ||||||
|  | 	loggedIn  bool | ||||||
|  | 	wg        *sync.WaitGroup | ||||||
|  |  | ||||||
|  | 	session        *Session | ||||||
|  | 	sessionLock    uint32 | ||||||
|  | 	handler        []Handler | ||||||
|  | 	msgCount       int | ||||||
|  | 	msgTimeout     time.Duration | ||||||
|  | 	Info           *Info | ||||||
|  | 	Store          *Store | ||||||
|  | 	ServerLastSeen time.Time | ||||||
|  |  | ||||||
|  | 	longClientName  string | ||||||
|  | 	shortClientName string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type websocketWrapper struct { | ||||||
|  | 	sync.Mutex | ||||||
|  | 	conn  *websocket.Conn | ||||||
|  | 	close chan struct{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type listenerWrapper struct { | ||||||
|  | 	sync.RWMutex | ||||||
|  | 	m map[string]chan string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established. | ||||||
|  | The goroutine for handling incoming messages is started | ||||||
|  | */ | ||||||
|  | func NewConn(timeout time.Duration) (*Conn, error) { | ||||||
|  | 	wac := &Conn{ | ||||||
|  | 		handler:    make([]Handler, 0), | ||||||
|  | 		msgCount:   0, | ||||||
|  | 		msgTimeout: timeout, | ||||||
|  | 		Store:      newStore(), | ||||||
|  |  | ||||||
|  | 		longClientName:  "github.com/rhymen/go-whatsapp", | ||||||
|  | 		shortClientName: "go-whatsapp", | ||||||
|  | 	} | ||||||
|  | 	return wac, wac.connect() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // connect should be guarded with wsWriteMutex | ||||||
|  | func (wac *Conn) connect() (err error) { | ||||||
|  | 	if wac.connected { | ||||||
|  | 		return ErrAlreadyConnected | ||||||
|  | 	} | ||||||
|  | 	wac.connected = true | ||||||
|  | 	defer func() { // set connected to false on error | ||||||
|  | 		if err != nil { | ||||||
|  | 			wac.connected = false | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	dialer := &websocket.Dialer{ | ||||||
|  | 		ReadBufferSize:   25 * 1024 * 1024, | ||||||
|  | 		WriteBufferSize:  10 * 1024 * 1024, | ||||||
|  | 		HandshakeTimeout: wac.msgTimeout, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}} | ||||||
|  | 	wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.Wrap(err, "couldn't dial whatsapp web websocket") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wsConn.SetCloseHandler(func(code int, text string) error { | ||||||
|  | 		// from default CloseHandler | ||||||
|  | 		message := websocket.FormatCloseMessage(code, "") | ||||||
|  | 		err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second)) | ||||||
|  |  | ||||||
|  | 		// our close handling | ||||||
|  | 		_, _ = wac.Disconnect() | ||||||
|  | 		wac.handle(&ErrConnectionClosed{Code: code, Text: text}) | ||||||
|  | 		return err | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	wac.ws = &websocketWrapper{ | ||||||
|  | 		conn:  wsConn, | ||||||
|  | 		close: make(chan struct{}), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wac.listener = &listenerWrapper{ | ||||||
|  | 		m: make(map[string]chan string), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wac.wg = &sync.WaitGroup{} | ||||||
|  | 	wac.wg.Add(2) | ||||||
|  | 	go wac.readPump() | ||||||
|  | 	go wac.keepAlive(20000, 60000) | ||||||
|  |  | ||||||
|  | 	wac.loggedIn = false | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) Disconnect() (Session, error) { | ||||||
|  | 	if !wac.connected { | ||||||
|  | 		return Session{}, ErrNotConnected | ||||||
|  | 	} | ||||||
|  | 	wac.connected = false | ||||||
|  | 	wac.loggedIn = false | ||||||
|  |  | ||||||
|  | 	close(wac.ws.close) //signal close | ||||||
|  | 	wac.wg.Wait()       //wait for close | ||||||
|  |  | ||||||
|  | 	err := wac.ws.conn.Close() | ||||||
|  | 	wac.ws = nil | ||||||
|  |  | ||||||
|  | 	if wac.session == nil { | ||||||
|  | 		return Session{}, err | ||||||
|  | 	} | ||||||
|  | 	return *wac.session, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) { | ||||||
|  | 	defer wac.wg.Done() | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		err := wac.sendKeepAlive() | ||||||
|  | 		if err != nil { | ||||||
|  | 			wac.handle(errors.Wrap(err, "keepAlive failed")) | ||||||
|  | 			//TODO: Consequences? | ||||||
|  | 		} | ||||||
|  | 		interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs | ||||||
|  | 		select { | ||||||
|  | 		case <-time.After(time.Duration(interval) * time.Millisecond): | ||||||
|  | 		case <-wac.ws.close: | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -2,7 +2,7 @@ package whatsapp | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary" | 	"github.com/Rhymen/go-whatsapp/binary" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| @@ -18,21 +18,21 @@ const ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| //TODO: filename? WhatsApp uses Store.Contacts for these functions | //TODO: filename? WhatsApp uses Store.Contacts for these functions | ||||||
| //TODO: functions probably shouldn't return a string, maybe build a struct / return json | // functions probably shouldn't return a string, maybe build a struct / return json | ||||||
| //TODO: check for further queries | // check for further queries | ||||||
| func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) { | func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) { | ||||||
| 	data := []interface{}{"query", "ProfilePicThumb", jid} | 	data := []interface{}{"query", "ProfilePicThumb", jid} | ||||||
| 	return wac.write(data) | 	return wac.writeJson(data) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) GetStatus(jid string) (<-chan string, error) { | func (wac *Conn) GetStatus(jid string) (<-chan string, error) { | ||||||
| 	data := []interface{}{"query", "Status", jid} | 	data := []interface{}{"query", "Status", jid} | ||||||
| 	return wac.write(data) | 	return wac.writeJson(data) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) { | func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) { | ||||||
| 	data := []interface{}{"action", "presence", "subscribe", jid} | 	data := []interface{}{"action", "presence", "subscribe", jid} | ||||||
| 	return wac.write(data) | 	return wac.writeJson(data) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) { | func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) { | ||||||
| @@ -84,7 +84,7 @@ func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error) | |||||||
| 
 | 
 | ||||||
| func (wac *Conn) Exist(jid string) (<-chan string, error) { | func (wac *Conn) Exist(jid string) (<-chan string, error) { | ||||||
| 	data := []interface{}{"query", "exist", jid} | 	data := []interface{}{"query", "exist", jid} | ||||||
| 	return wac.write(data) | 	return wac.writeJson(data) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) Emoji() (*binary.Node, error) { | func (wac *Conn) Emoji() (*binary.Node, error) { | ||||||
| @@ -9,7 +9,6 @@ second stage "expands" this key into several additional pseudorandom keys (the o | |||||||
| package hkdf | package hkdf | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/hmac" |  | ||||||
| 	"crypto/sha256" | 	"crypto/sha256" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"golang.org/x/crypto/hkdf" | 	"golang.org/x/crypto/hkdf" | ||||||
| @@ -20,33 +19,29 @@ import ( | |||||||
| Expand expands a given key with the HKDF algorithm. | Expand expands a given key with the HKDF algorithm. | ||||||
| */ | */ | ||||||
| func Expand(key []byte, length int, info string) ([]byte, error) { | func Expand(key []byte, length int, info string) ([]byte, error) { | ||||||
|  | 	var h io.Reader | ||||||
| 	if info == "" { | 	if info == "" { | ||||||
| 		keyBlock := hmac.New(sha256.New, key) | 		/* | ||||||
| 		var out, last []byte | 			Only used during initial login | ||||||
| 
 | 			Pseudorandom Key is provided by server and has not to be created | ||||||
| 		var blockIndex byte = 1 | 		*/ | ||||||
| 		for i := 0; len(out) < length; i++ { | 		h = hkdf.Expand(sha256.New, key, []byte(info)) | ||||||
| 			keyBlock.Reset() |  | ||||||
| 			//keyBlock.Write(append(append(last, []byte(info)...), blockIndex)) |  | ||||||
| 			keyBlock.Write(last) |  | ||||||
| 			keyBlock.Write([]byte(info)) |  | ||||||
| 			keyBlock.Write([]byte{blockIndex}) |  | ||||||
| 			last = keyBlock.Sum(nil) |  | ||||||
| 			blockIndex += 1 |  | ||||||
| 			out = append(out, last...) |  | ||||||
| 		} |  | ||||||
| 		return out[:length], nil |  | ||||||
| 	} else { | 	} else { | ||||||
| 		h := hkdf.New(sha256.New, key, nil, []byte(info)) | 		/* | ||||||
| 		out := make([]byte, length) | 			Used every other time | ||||||
| 		n, err := io.ReadAtLeast(h, out, length) | 			Pseudorandom Key is created during kdf.New | ||||||
| 		if err != nil { | 			This is the normal that crypto/hkdf is used | ||||||
| 			return nil, err | 		*/ | ||||||
| 		} | 		h = hkdf.New(sha256.New, key, nil, []byte(info)) | ||||||
| 		if n != length { |  | ||||||
| 			return nil, fmt.Errorf("new key to short") |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return out[:length], nil |  | ||||||
| 	} | 	} | ||||||
|  | 	out := make([]byte, length) | ||||||
|  | 	n, err := io.ReadAtLeast(h, out, length) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if n != length { | ||||||
|  | 		return nil, fmt.Errorf("new key to short") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return out, nil | ||||||
| } | } | ||||||
							
								
								
									
										35
									
								
								vendor/github.com/Rhymen/go-whatsapp/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/Rhymen/go-whatsapp/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | package whatsapp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	ErrAlreadyConnected  = errors.New("already connected") | ||||||
|  | 	ErrAlreadyLoggedIn   = errors.New("already logged in") | ||||||
|  | 	ErrInvalidSession    = errors.New("invalid session") | ||||||
|  | 	ErrLoginInProgress   = errors.New("login or restore already running") | ||||||
|  | 	ErrNotConnected      = errors.New("not connected") | ||||||
|  | 	ErrInvalidWsData     = errors.New("received invalid data") | ||||||
|  | 	ErrConnectionTimeout = errors.New("connection timed out") | ||||||
|  | 	ErrMissingMessageTag = errors.New("no messageTag specified or to short") | ||||||
|  | 	ErrInvalidHmac       = errors.New("invalid hmac") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ErrConnectionFailed struct { | ||||||
|  | 	Err error | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *ErrConnectionFailed) Error() string { | ||||||
|  | 	return fmt.Sprintf("connection to WhatsApp servers failed: %v", e.Err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ErrConnectionClosed struct { | ||||||
|  | 	Code int | ||||||
|  | 	Text string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *ErrConnectionClosed) Error() string { | ||||||
|  | 	return fmt.Sprintf("server closed connection,code: %d,text: %s", e.Code, e.Text) | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | module github.com/Rhymen/go-whatsapp | ||||||
|  |  | ||||||
|  | require ( | ||||||
|  | 	github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d // indirect | ||||||
|  | 	github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d // indirect | ||||||
|  | 	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/pkg/errors v0.8.1 | ||||||
|  | 	golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 | ||||||
|  | ) | ||||||
							
								
								
									
										35
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | 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/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= | ||||||
|  | github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d h1:m3wkrunHupL9XzzM+JZu1pgoDV1d9LFtD0gedNTHVDU= | ||||||
|  | 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 h1:muQlzqfZxjptOBjPdv+UoxVMr8Y1rPx7VMGPJIAFc5w= | ||||||
|  | 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 h1:xP//3V77YvHd1cj2Z3ttuQWAvs5WmIwBbjKe/t0g/tM= | ||||||
|  | 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 h1:IRmRE0SPMByczwE2dhnTcVojje3w2TCSKwFrboLUbDg= | ||||||
|  | github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM= | ||||||
|  | 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/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= | ||||||
|  | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||||
|  | 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/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE= | ||||||
|  | github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= | ||||||
|  | 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/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= | ||||||
|  | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
|  | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= | ||||||
|  | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | 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= | ||||||
|  | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) { | func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) { | ||||||
| 	data := []interface{}{"query", "GroupMetadata", jid} | 	data := []interface{}{"query", "GroupMetadata", jid} | ||||||
| 	return wac.write(data) | 	return wac.writeJson(data) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) { | func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) { | ||||||
| @@ -41,7 +41,7 @@ func (wac *Conn) LeaveGroup(jid string) (<-chan string, error) { | |||||||
| 
 | 
 | ||||||
| func (wac *Conn) GroupInviteLink(jid string) (string, error) { | func (wac *Conn) GroupInviteLink(jid string) (string, error) { | ||||||
| 	request := []interface{}{"query", "inviteCode", jid} | 	request := []interface{}{"query", "inviteCode", jid} | ||||||
| 	ch, err := wac.write(request) | 	ch, err := wac.writeJson(request) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| @@ -63,3 +63,28 @@ func (wac *Conn) GroupInviteLink(jid string) (string, error) { | |||||||
| 
 | 
 | ||||||
| 	return response["code"].(string), nil | 	return response["code"].(string), nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (wac *Conn) GroupAcceptInviteCode(code string) (jid string, err error) { | ||||||
|  | 	request := []interface{}{"action", "invite", code} | ||||||
|  | 	ch, err := wac.writeJson(request) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var response map[string]interface{} | ||||||
|  | 
 | ||||||
|  | 	select { | ||||||
|  | 	case r := <-ch: | ||||||
|  | 		if err := json.Unmarshal([]byte(r), &response); err != nil { | ||||||
|  | 			return "", fmt.Errorf("error decoding response message: %v\n", err) | ||||||
|  | 		} | ||||||
|  | 	case <-time.After(wac.msgTimeout): | ||||||
|  | 		return "", fmt.Errorf("request timed out") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if int(response["status"].(float64)) != 200 { | ||||||
|  | 		return "", fmt.Errorf("request responded with %d", response["status"]) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return response["gid"].(string), nil | ||||||
|  | } | ||||||
| @@ -2,9 +2,11 @@ package whatsapp | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary" |  | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary/proto" |  | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/binary" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/binary/proto" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| @@ -78,6 +80,22 @@ type RawMessageHandler interface { | |||||||
| 	HandleRawMessage(message *proto.WebMessageInfo) | 	HandleRawMessage(message *proto.WebMessageInfo) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  | The ContactListHandler interface needs to be implemented to applky custom actions to contact lists dispatched by the dispatcher. | ||||||
|  | */ | ||||||
|  | type ContactListHandler interface { | ||||||
|  | 	Handler | ||||||
|  | 	HandleContactList(contacts []Contact) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  | The ChatListHandler interface needs to be implemented to apply custom actions to chat lists dispatched by the dispatcher. | ||||||
|  | */ | ||||||
|  | type ChatListHandler interface { | ||||||
|  | 	Handler | ||||||
|  | 	HandleChatList(contacts []Chat) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* | /* | ||||||
| AddHandler adds an handler to the list of handler that receive dispatched messages. | AddHandler adds an handler to the list of handler that receive dispatched messages. | ||||||
| The provided handler must at least implement the Handler interface. Additionally implemented | The provided handler must at least implement the Handler interface. Additionally implemented | ||||||
| @@ -88,6 +106,27 @@ func (wac *Conn) AddHandler(handler Handler) { | |||||||
| 	wac.handler = append(wac.handler, handler) | 	wac.handler = append(wac.handler, handler) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // RemoveHandler removes a handler from the list of handlers that receive dispatched messages. | ||||||
|  | func (wac *Conn) RemoveHandler(handler Handler) bool { | ||||||
|  | 	i := -1 | ||||||
|  | 	for k, v := range wac.handler { | ||||||
|  | 		if v == handler { | ||||||
|  | 			i = k | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if i > -1 { | ||||||
|  | 		wac.handler = append(wac.handler[:i], wac.handler[i+1:]...) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // RemoveHandlers empties the list of handlers that receive dispatched messages. | ||||||
|  | func (wac *Conn) RemoveHandlers() { | ||||||
|  | 	wac.handler = make([]Handler, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (wac *Conn) handle(message interface{}) { | func (wac *Conn) handle(message interface{}) { | ||||||
| 	switch m := message.(type) { | 	switch m := message.(type) { | ||||||
| 	case error: | 	case error: | ||||||
| @@ -140,6 +179,62 @@ func (wac *Conn) handle(message interface{}) { | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (wac *Conn) handleContacts(contacts interface{}) { | ||||||
|  | 	var contactList []Contact | ||||||
|  | 	c, ok := contacts.([]interface{}) | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	for _, contact := range c { | ||||||
|  | 		contactNode, ok := contact.(binary.Node) | ||||||
|  | 		if !ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1) | ||||||
|  | 		contactList = append(contactList, Contact{ | ||||||
|  | 			jid, | ||||||
|  | 			contactNode.Attributes["notify"], | ||||||
|  | 			contactNode.Attributes["name"], | ||||||
|  | 			contactNode.Attributes["short"], | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 	for _, h := range wac.handler { | ||||||
|  | 		if x, ok := h.(ContactListHandler); ok { | ||||||
|  | 			go x.HandleContactList(contactList) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (wac *Conn) handleChats(chats interface{}) { | ||||||
|  | 	var chatList []Chat | ||||||
|  | 	c, ok := chats.([]interface{}) | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	for _, chat := range c { | ||||||
|  | 		chatNode, ok := chat.(binary.Node) | ||||||
|  | 		if !ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1) | ||||||
|  | 		chatList = append(chatList, Chat{ | ||||||
|  | 			jid, | ||||||
|  | 			chatNode.Attributes["name"], | ||||||
|  | 			chatNode.Attributes["count"], | ||||||
|  | 			chatNode.Attributes["t"], | ||||||
|  | 			chatNode.Attributes["mute"], | ||||||
|  | 			chatNode.Attributes["spam"], | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 	for _, h := range wac.handler { | ||||||
|  | 		if x, ok := h.(ChatListHandler); ok { | ||||||
|  | 			go x.HandleChatList(chatList) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (wac *Conn) dispatch(msg interface{}) { | func (wac *Conn) dispatch(msg interface{}) { | ||||||
| 	if msg == nil { | 	if msg == nil { | ||||||
| 		return | 		return | ||||||
| @@ -158,6 +253,10 @@ func (wac *Conn) dispatch(msg interface{}) { | |||||||
| 			} | 			} | ||||||
| 		} else if message.Description == "response" && message.Attributes["type"] == "contacts" { | 		} else if message.Description == "response" && message.Attributes["type"] == "contacts" { | ||||||
| 			wac.updateContacts(message.Content) | 			wac.updateContacts(message.Content) | ||||||
|  | 			wac.handleContacts(message.Content) | ||||||
|  | 		} else if message.Description == "response" && message.Attributes["type"] == "chat" { | ||||||
|  | 			wac.updateChats(message.Content) | ||||||
|  | 			wac.handleChats(message.Content) | ||||||
| 		} | 		} | ||||||
| 	case error: | 	case error: | ||||||
| 		wac.handle(message) | 		wac.handle(message) | ||||||
| @@ -8,8 +8,8 @@ import ( | |||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/matterbridge/go-whatsapp/crypto/cbc" | 	"github.com/Rhymen/go-whatsapp/crypto/cbc" | ||||||
| 	"github.com/matterbridge/go-whatsapp/crypto/hkdf" | 	"github.com/Rhymen/go-whatsapp/crypto/hkdf" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"mime/multipart" | 	"mime/multipart" | ||||||
| @@ -133,7 +133,7 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)} | 	uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)} | ||||||
| 	ch, err := wac.write(uploadReq) | 	ch, err := wac.writeJson(uploadReq) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", nil, nil, nil, 0, err | 		return "", nil, nil, nil, 0, err | ||||||
| 	} | 	} | ||||||
| @@ -4,8 +4,8 @@ import ( | |||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary" | 	"github.com/Rhymen/go-whatsapp/binary" | ||||||
| 	"github.com/matterbridge/go-whatsapp/binary/proto" | 	"github.com/Rhymen/go-whatsapp/binary/proto" | ||||||
| 	"io" | 	"io" | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| @@ -22,61 +22,77 @@ const ( | |||||||
| 	MediaDocument MediaType = "WhatsApp Document Keys" | 	MediaDocument MediaType = "WhatsApp Document Keys" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) Send(msg interface{}) error { | var msgInfo MessageInfo | ||||||
|  | 
 | ||||||
|  | func (wac *Conn) Send(msg interface{}) (string, error) { | ||||||
| 	var err error | 	var err error | ||||||
| 	var ch <-chan string | 	var ch <-chan string | ||||||
|  | 	var msgProto *proto.WebMessageInfo | ||||||
| 
 | 
 | ||||||
| 	switch m := msg.(type) { | 	switch m := msg.(type) { | ||||||
| 	case *proto.WebMessageInfo: | 	case *proto.WebMessageInfo: | ||||||
| 		ch, err = wac.sendProto(m) | 		ch, err = wac.sendProto(m) | ||||||
| 	case TextMessage: | 	case TextMessage: | ||||||
| 		ch, err = wac.sendProto(getTextProto(m)) | 		msgProto = getTextProto(m) | ||||||
|  | 		msgInfo = getMessageInfo(msgProto) | ||||||
|  | 		ch, err = wac.sendProto(msgProto) | ||||||
| 	case ImageMessage: | 	case ImageMessage: | ||||||
| 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage) | 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("image upload failed: %v", err) | 			return "ERROR", fmt.Errorf("image upload failed: %v", err) | ||||||
| 		} | 		} | ||||||
| 		ch, err = wac.sendProto(getImageProto(m)) | 		msgProto = getImageProto(m) | ||||||
|  | 		msgInfo = getMessageInfo(msgProto) | ||||||
|  | 		ch, err = wac.sendProto(msgProto) | ||||||
| 	case VideoMessage: | 	case VideoMessage: | ||||||
| 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo) | 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("video upload failed: %v", err) | 			return "ERROR", fmt.Errorf("video upload failed: %v", err) | ||||||
| 		} | 		} | ||||||
| 		ch, err = wac.sendProto(getVideoProto(m)) | 		msgProto = getVideoProto(m) | ||||||
|  | 		msgInfo = getMessageInfo(msgProto) | ||||||
|  | 		ch, err = wac.sendProto(msgProto) | ||||||
| 	case DocumentMessage: | 	case DocumentMessage: | ||||||
| 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument) | 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("document upload failed: %v", err) | 			return "ERROR", fmt.Errorf("document upload failed: %v", err) | ||||||
| 		} | 		} | ||||||
| 		ch, err = wac.sendProto(getDocumentProto(m)) | 		msgProto = getDocumentProto(m) | ||||||
|  | 		msgInfo = getMessageInfo(msgProto) | ||||||
|  | 		ch, err = wac.sendProto(msgProto) | ||||||
| 	case AudioMessage: | 	case AudioMessage: | ||||||
| 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio) | 		m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("audio upload failed: %v", err) | 			return "ERROR", fmt.Errorf("audio upload failed: %v", err) | ||||||
| 		} | 		} | ||||||
| 		ch, err = wac.sendProto(getAudioProto(m)) | 		msgProto = getAudioProto(m) | ||||||
|  | 		msgInfo = getMessageInfo(msgProto) | ||||||
|  | 		ch, err = wac.sendProto(msgProto) | ||||||
| 	default: | 	default: | ||||||
| 		return fmt.Errorf("cannot match type %T, use message types declared in the package", msg) | 		return "ERROR", fmt.Errorf("cannot match type %T, use message types declared in the package", msg) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("could not send proto: %v", err) | 		return "ERROR", fmt.Errorf("could not send proto: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	select { | 	select { | ||||||
| 	case response := <-ch: | 	case response := <-ch: | ||||||
| 		var resp map[string]interface{} | 		var resp map[string]interface{} | ||||||
| 		if err = json.Unmarshal([]byte(response), &resp); err != nil { | 		if err = json.Unmarshal([]byte(response), &resp); err != nil { | ||||||
| 			return fmt.Errorf("error decoding sending response: %v\n", err) | 			return "ERROR", fmt.Errorf("error decoding sending response: %v\n", err) | ||||||
| 		} | 		} | ||||||
| 		if int(resp["status"].(float64)) != 200 { | 		if int(resp["status"].(float64)) != 200 { | ||||||
| 			return fmt.Errorf("message sending responded with %d", resp["status"]) | 			return "ERROR", fmt.Errorf("message sending responded with %d", resp["status"]) | ||||||
|  | 		} | ||||||
|  | 		if int(resp["status"].(float64)) == 200 { | ||||||
|  | 			return msgInfo.Id, nil | ||||||
| 		} | 		} | ||||||
| 	case <-time.After(wac.msgTimeout): | 	case <-time.After(wac.msgTimeout): | ||||||
| 		return fmt.Errorf("sending message timed out") | 		return "ERROR", fmt.Errorf("sending message timed out") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return "ERROR", nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) { | func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) { | ||||||
							
								
								
									
										111
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | package whatsapp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/hmac" | ||||||
|  | 	"crypto/sha256" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/binary" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/crypto/cbc" | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (wac *Conn) readPump() { | ||||||
|  | 	defer wac.wg.Done() | ||||||
|  |  | ||||||
|  | 	var readErr error | ||||||
|  | 	var msgType int | ||||||
|  | 	var reader io.Reader | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		readerFound := make(chan struct{}) | ||||||
|  | 		go func() { | ||||||
|  | 			msgType, reader, readErr = wac.ws.conn.NextReader() | ||||||
|  | 			close(readerFound) | ||||||
|  | 		}() | ||||||
|  | 		select { | ||||||
|  | 		case <-readerFound: | ||||||
|  | 			if readErr != nil { | ||||||
|  | 				wac.handle(&ErrConnectionFailed{Err: readErr}) | ||||||
|  | 				_, _ = wac.Disconnect() | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			msg, err := ioutil.ReadAll(reader) | ||||||
|  | 			if err != nil { | ||||||
|  | 				wac.handle(errors.Wrap(err, "error reading message from Reader")) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			err = wac.processReadData(msgType, msg) | ||||||
|  | 			if err != nil { | ||||||
|  | 				wac.handle(errors.Wrap(err, "error processing data")) | ||||||
|  | 			} | ||||||
|  | 		case <-wac.ws.close: | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) processReadData(msgType int, msg []byte) error { | ||||||
|  | 	data := strings.SplitN(string(msg), ",", 2) | ||||||
|  |  | ||||||
|  | 	if data[0][0] == '!' { //Keep-Alive Timestamp | ||||||
|  | 		data = append(data, data[0][1:]) //data[1] | ||||||
|  | 		data[0] = "!" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(data) != 2 || len(data[1]) == 0 { | ||||||
|  | 		return ErrInvalidWsData | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wac.listener.RLock() | ||||||
|  | 	listener, hasListener := wac.listener.m[data[0]] | ||||||
|  | 	wac.listener.RUnlock() | ||||||
|  |  | ||||||
|  | 	if hasListener { | ||||||
|  | 		// listener only exists for TextMessages query messages out of contact.go | ||||||
|  | 		// If these binary query messages can be handled another way, | ||||||
|  | 		// then the TextMessages, which are all JSON encoded, can directly | ||||||
|  | 		// be unmarshalled. The listener chan could then be changed from type | ||||||
|  | 		// chan string to something like chan map[string]interface{}. The unmarshalling | ||||||
|  | 		// in several places, especially in session.go, would then be gone. | ||||||
|  | 		listener <- data[1] | ||||||
|  |  | ||||||
|  | 		wac.listener.Lock() | ||||||
|  | 		delete(wac.listener.m, data[0]) | ||||||
|  | 		wac.listener.Unlock() | ||||||
|  | 	} else if msgType == websocket.BinaryMessage && wac.loggedIn { | ||||||
|  | 		message, err := wac.decryptBinaryMessage([]byte(data[1])) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "error decoding binary") | ||||||
|  | 		} | ||||||
|  | 		wac.dispatch(message) | ||||||
|  | 	} else { //RAW json status updates | ||||||
|  | 		wac.handle(string(data[1])) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) { | ||||||
|  | 	//message validation | ||||||
|  | 	h2 := hmac.New(sha256.New, wac.session.MacKey) | ||||||
|  | 	h2.Write([]byte(msg[32:])) | ||||||
|  | 	if !hmac.Equal(h2.Sum(nil), msg[:32]) { | ||||||
|  | 		return nil, ErrInvalidHmac | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// message decrypt | ||||||
|  | 	d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "decrypting message with AES-CBC failed") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// message unmarshal | ||||||
|  | 	message, err := binary.Unmarshal(d) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "could not decode binary") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return message, nil | ||||||
|  | } | ||||||
| @@ -7,16 +7,20 @@ import ( | |||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/matterbridge/go-whatsapp/crypto/cbc" | 	"github.com/Rhymen/go-whatsapp/crypto/cbc" | ||||||
| 	"github.com/matterbridge/go-whatsapp/crypto/curve25519" | 	"github.com/Rhymen/go-whatsapp/crypto/curve25519" | ||||||
| 	"github.com/matterbridge/go-whatsapp/crypto/hkdf" | 	"github.com/Rhymen/go-whatsapp/crypto/hkdf" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | //represents the WhatsAppWeb client version | ||||||
|  | var waVersion = []int{0, 3, 3324} | ||||||
|  | 
 | ||||||
| /* | /* | ||||||
| Session contains session individual information. To be able to resume the connection without scanning the qr code | Session contains session individual information. To be able to resume the connection without scanning the qr code | ||||||
| every time you should save the Session returned by Login and use RestoreSession the next time you want to login. | every time you should save the Session returned by Login and use RestoreWithSession the next time you want to login. | ||||||
| Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after | Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after | ||||||
| every re-login and should be saved every time. | every re-login and should be saved every time. | ||||||
| */ | */ | ||||||
| @@ -98,7 +102,7 @@ func (wac *Conn) SetClientName(long, short string) error { | |||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code | Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code | ||||||
| every time, you should save the returned session and use RestoreSession the next time. Login takes a writable channel | every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel | ||||||
| as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data | as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data | ||||||
| should be displayed as an qr code in a way you prefer. To print a qr code to console you can use: | should be displayed as an qr code in a way you prefer. To print a qr code to console you can use: | ||||||
| github.com/Baozisoftware/qrcode-terminal-go Example login procedure: | github.com/Baozisoftware/qrcode-terminal-go Example login procedure: | ||||||
| @@ -121,7 +125,21 @@ github.com/Baozisoftware/qrcode-terminal-go Example login procedure: | |||||||
| */ | */ | ||||||
| func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | ||||||
| 	session := Session{} | 	session := Session{} | ||||||
|  | 	//Makes sure that only a single Login or Restore can happen at the same time | ||||||
|  | 	if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) { | ||||||
|  | 		return session, ErrLoginInProgress | ||||||
|  | 	} | ||||||
|  | 	defer atomic.StoreUint32(&wac.sessionLock, 0) | ||||||
| 
 | 
 | ||||||
|  | 	if wac.loggedIn { | ||||||
|  | 		return session, ErrAlreadyLoggedIn | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := wac.connect(); err != nil && err != ErrAlreadyConnected { | ||||||
|  | 		return session, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	//logged in?!? | ||||||
| 	if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) { | 	if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) { | ||||||
| 		return session, fmt.Errorf("already logged in") | 		return session, fmt.Errorf("already logged in") | ||||||
| 	} | 	} | ||||||
| @@ -133,9 +151,8 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	session.ClientId = base64.StdEncoding.EncodeToString(clientId) | 	session.ClientId = base64.StdEncoding.EncodeToString(clientId) | ||||||
| 	//oldVersion=8691 | 	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true} | ||||||
| 	login := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true} | 	loginChan, err := wac.writeJson(login) | ||||||
| 	loginChan, err := wac.write(login) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return session, fmt.Errorf("error writing login: %v\n", err) | 		return session, fmt.Errorf("error writing login: %v\n", err) | ||||||
| 	} | 	} | ||||||
| @@ -160,14 +177,16 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//listener for Login response | 	//listener for Login response | ||||||
| 	messageTag := "s1" | 	s1 := make(chan string, 1) | ||||||
| 	wac.listener[messageTag] = make(chan string, 1) | 	wac.listener.Lock() | ||||||
|  | 	wac.listener.m["s1"] = s1 | ||||||
|  | 	wac.listener.Unlock() | ||||||
| 
 | 
 | ||||||
| 	qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId) | 	qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId) | ||||||
| 
 | 
 | ||||||
| 	var resp2 []interface{} | 	var resp2 []interface{} | ||||||
| 	select { | 	select { | ||||||
| 	case r1 := <-wac.listener[messageTag]: | 	case r1 := <-s1: | ||||||
| 		if err := json.Unmarshal([]byte(r1), &resp2); err != nil { | 		if err := json.Unmarshal([]byte(r1), &resp2); err != nil { | ||||||
| 			return session, fmt.Errorf("error decoding qr code resp: %v", err) | 			return session, fmt.Errorf("error decoding qr code resp: %v", err) | ||||||
| 		} | 		} | ||||||
| @@ -226,90 +245,136 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | |||||||
| 	session.EncKey = keyDecrypted[:32] | 	session.EncKey = keyDecrypted[:32] | ||||||
| 	session.MacKey = keyDecrypted[32:64] | 	session.MacKey = keyDecrypted[32:64] | ||||||
| 	wac.session = &session | 	wac.session = &session | ||||||
|  | 	wac.loggedIn = true | ||||||
| 
 | 
 | ||||||
| 	return session, nil | 	return session, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | //TODO: GoDoc | ||||||
| /* | /* | ||||||
| RestoreSession is the function that restores a given session. It will try to reestablish the connection to the | Basically the old RestoreSession functionality | ||||||
|  | */ | ||||||
|  | func (wac *Conn) RestoreWithSession(session Session) (_ Session, err error) { | ||||||
|  | 	if wac.loggedIn { | ||||||
|  | 		return Session{}, ErrAlreadyLoggedIn | ||||||
|  | 	} | ||||||
|  | 	old := wac.session | ||||||
|  | 	defer func() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			wac.session = old | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	wac.session = &session | ||||||
|  | 
 | ||||||
|  | 	if err = wac.Restore(); err != nil { | ||||||
|  | 		wac.session = nil | ||||||
|  | 		return Session{}, err | ||||||
|  | 	} | ||||||
|  | 	return *wac.session, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*//TODO: GoDoc | ||||||
|  | RestoreWithSession is the function that restores a given session. It will try to reestablish the connection to the | ||||||
| WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be | WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be | ||||||
| saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not | saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not | ||||||
| suggested. If so, a challenge has to be resolved which is just another possible point of failure. | suggested. If so, a challenge has to be resolved which is just another possible point of failure. | ||||||
| */ | */ | ||||||
| func (wac *Conn) RestoreSession(session Session) (Session, error) { | func (wac *Conn) Restore() error { | ||||||
| 	if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) { | 	//Makes sure that only a single Login or Restore can happen at the same time | ||||||
| 		return Session{}, fmt.Errorf("already logged in") | 	if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) { | ||||||
|  | 		return ErrLoginInProgress | ||||||
|  | 	} | ||||||
|  | 	defer atomic.StoreUint32(&wac.sessionLock, 0) | ||||||
|  | 
 | ||||||
|  | 	if wac.session == nil { | ||||||
|  | 		return ErrInvalidSession | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	wac.session = &session | 	if err := wac.connect(); err != nil && err != ErrAlreadyConnected { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if wac.loggedIn { | ||||||
|  | 		return ErrAlreadyLoggedIn | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	//listener for Conn or challenge; s1 is not allowed to drop | 	//listener for Conn or challenge; s1 is not allowed to drop | ||||||
| 	wac.listener["s1"] = make(chan string, 1) | 	s1 := make(chan string, 1) | ||||||
|  | 	wac.listener.Lock() | ||||||
|  | 	wac.listener.m["s1"] = s1 | ||||||
|  | 	wac.listener.Unlock() | ||||||
| 
 | 
 | ||||||
| 	//admin init | 	//admin init | ||||||
| 	init := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true} | 	init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true} | ||||||
| 	initChan, err := wac.write(init) | 	initChan, err := wac.writeJson(init) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		wac.session = nil | 		return fmt.Errorf("error writing admin init: %v\n", err) | ||||||
| 		return Session{}, fmt.Errorf("error writing admin init: %v\n", err) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//admin login with takeover | 	//admin login with takeover | ||||||
| 	login := []interface{}{"admin", "login", session.ClientToken, session.ServerToken, session.ClientId, "takeover"} | 	login := []interface{}{"admin", "login", wac.session.ClientToken, wac.session.ServerToken, wac.session.ClientId, "takeover"} | ||||||
| 	loginChan, err := wac.write(login) | 	loginChan, err := wac.writeJson(login) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		wac.session = nil | 		return fmt.Errorf("error writing admin login: %v\n", err) | ||||||
| 		return Session{}, fmt.Errorf("error writing admin login: %v\n", err) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	select { | 	select { | ||||||
| 	case r := <-initChan: | 	case r := <-initChan: | ||||||
| 		var resp map[string]interface{} | 		var resp map[string]interface{} | ||||||
| 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||||
| 			wac.session = nil | 			return fmt.Errorf("error decoding login connResp: %v\n", err) | ||||||
| 			return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err) |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if int(resp["status"].(float64)) != 200 { | 		if int(resp["status"].(float64)) != 200 { | ||||||
| 			wac.session = nil | 			return fmt.Errorf("init responded with %d", resp["status"]) | ||||||
| 			return Session{}, fmt.Errorf("init responded with %d", resp["status"]) |  | ||||||
| 		} | 		} | ||||||
| 	case <-time.After(wac.msgTimeout): | 	case <-time.After(wac.msgTimeout): | ||||||
| 		wac.session = nil | 		return fmt.Errorf("restore session init timed out") | ||||||
| 		return Session{}, fmt.Errorf("restore session init timed out") |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//wait for s1 | 	//wait for s1 | ||||||
| 	var connResp []interface{} | 	var connResp []interface{} | ||||||
| 	select { | 	select { | ||||||
| 	case r1 := <-wac.listener["s1"]: | 	case r1 := <-s1: | ||||||
| 		if err := json.Unmarshal([]byte(r1), &connResp); err != nil { | 		if err := json.Unmarshal([]byte(r1), &connResp); err != nil { | ||||||
| 			wac.session = nil | 			return fmt.Errorf("error decoding s1 message: %v\n", err) | ||||||
| 			return Session{}, fmt.Errorf("error decoding s1 message: %v\n", err) |  | ||||||
| 		} | 		} | ||||||
| 	case <-time.After(wac.msgTimeout): | 	case <-time.After(wac.msgTimeout): | ||||||
| 		wac.session = nil | 
 | ||||||
| 		return Session{}, fmt.Errorf("restore session connection timed out") | 		//check for an error message | ||||||
|  | 		select { | ||||||
|  | 		case r := <-loginChan: | ||||||
|  | 			var resp map[string]interface{} | ||||||
|  | 			if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||||
|  | 				return fmt.Errorf("error decoding login connResp: %v\n", err) | ||||||
|  | 			} | ||||||
|  | 			if int(resp["status"].(float64)) != 200 { | ||||||
|  | 				return fmt.Errorf("admin login responded with %d", int(resp["status"].(float64))) | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			// not even an error message – assume timeout | ||||||
|  | 			return fmt.Errorf("restore session connection timed out") | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//check if challenge is present | 	//check if challenge is present | ||||||
| 	if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" { | 	if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" { | ||||||
| 		wac.listener["s2"] = make(chan string, 1) | 		s2 := make(chan string, 1) | ||||||
|  | 		wac.listener.Lock() | ||||||
|  | 		wac.listener.m["s2"] = s2 | ||||||
|  | 		wac.listener.Unlock() | ||||||
| 
 | 
 | ||||||
| 		if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil { | 		if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil { | ||||||
| 			wac.session = nil | 			return fmt.Errorf("error resolving challenge: %v\n", err) | ||||||
| 			return Session{}, fmt.Errorf("error resolving challenge: %v\n", err) |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		select { | 		select { | ||||||
| 		case r := <-wac.listener["s2"]: | 		case r := <-s2: | ||||||
| 			if err := json.Unmarshal([]byte(r), &connResp); err != nil { | 			if err := json.Unmarshal([]byte(r), &connResp); err != nil { | ||||||
| 				wac.session = nil | 				return fmt.Errorf("error decoding s2 message: %v\n", err) | ||||||
| 				return Session{}, fmt.Errorf("error decoding s2 message: %v\n", err) |  | ||||||
| 			} | 			} | ||||||
| 		case <-time.After(wac.msgTimeout): | 		case <-time.After(wac.msgTimeout): | ||||||
| 			wac.session = nil | 			return fmt.Errorf("restore session challenge timed out") | ||||||
| 			return Session{}, fmt.Errorf("restore session challenge timed out") |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -318,17 +383,14 @@ func (wac *Conn) RestoreSession(session Session) (Session, error) { | |||||||
| 	case r := <-loginChan: | 	case r := <-loginChan: | ||||||
| 		var resp map[string]interface{} | 		var resp map[string]interface{} | ||||||
| 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||||
| 			wac.session = nil | 			return fmt.Errorf("error decoding login connResp: %v\n", err) | ||||||
| 			return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err) |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if int(resp["status"].(float64)) != 200 { | 		if int(resp["status"].(float64)) != 200 { | ||||||
| 			wac.session = nil | 			return fmt.Errorf("admin login responded with %d", resp["status"]) | ||||||
| 			return Session{}, fmt.Errorf("admin login responded with %d", resp["status"]) |  | ||||||
| 		} | 		} | ||||||
| 	case <-time.After(wac.msgTimeout): | 	case <-time.After(wac.msgTimeout): | ||||||
| 		wac.session = nil | 		return fmt.Errorf("restore session login timed out") | ||||||
| 		return Session{}, fmt.Errorf("restore session login timed out") |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	info := connResp[1].(map[string]interface{}) | 	info := connResp[1].(map[string]interface{}) | ||||||
| @@ -336,11 +398,12 @@ func (wac *Conn) RestoreSession(session Session) (Session, error) { | |||||||
| 	wac.Info = newInfoFromReq(info) | 	wac.Info = newInfoFromReq(info) | ||||||
| 
 | 
 | ||||||
| 	//set new tokens | 	//set new tokens | ||||||
| 	session.ClientToken = info["clientToken"].(string) | 	wac.session.ClientToken = info["clientToken"].(string) | ||||||
| 	session.ServerToken = info["serverToken"].(string) | 	wac.session.ServerToken = info["serverToken"].(string) | ||||||
| 	session.Wid = info["wid"].(string) | 	wac.session.Wid = info["wid"].(string) | ||||||
|  | 	wac.loggedIn = true | ||||||
| 
 | 
 | ||||||
| 	return *wac.session, nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (wac *Conn) resolveChallenge(challenge string) error { | func (wac *Conn) resolveChallenge(challenge string) error { | ||||||
| @@ -353,7 +416,7 @@ func (wac *Conn) resolveChallenge(challenge string) error { | |||||||
| 	h2.Write([]byte(decoded)) | 	h2.Write([]byte(decoded)) | ||||||
| 
 | 
 | ||||||
| 	ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId} | 	ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId} | ||||||
| 	challengeChan, err := wac.write(ch) | 	challengeChan, err := wac.writeJson(ch) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("error writing challenge: %v\n", err) | 		return fmt.Errorf("error writing challenge: %v\n", err) | ||||||
| 	} | 	} | ||||||
| @@ -380,7 +443,7 @@ The session can not be resumed and will disappear on your phone in the WhatsAppW | |||||||
| */ | */ | ||||||
| func (wac *Conn) Logout() error { | func (wac *Conn) Logout() error { | ||||||
| 	login := []interface{}{"admin", "Conn", "disconnect"} | 	login := []interface{}{"admin", "Conn", "disconnect"} | ||||||
| 	_, err := wac.write(login) | 	_, err := wac.writeJson(login) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("error writing logout: %v\n", err) | 		return fmt.Errorf("error writing logout: %v\n", err) | ||||||
| 	} | 	} | ||||||
							
								
								
									
										80
									
								
								vendor/github.com/Rhymen/go-whatsapp/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								vendor/github.com/Rhymen/go-whatsapp/store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | package whatsapp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/binary" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Store struct { | ||||||
|  | 	Contacts map[string]Contact | ||||||
|  | 	Chats    map[string]Chat | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Contact struct { | ||||||
|  | 	Jid    string | ||||||
|  | 	Notify string | ||||||
|  | 	Name   string | ||||||
|  | 	Short  string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Chat struct { | ||||||
|  | 	Jid             string | ||||||
|  | 	Name            string | ||||||
|  | 	Unread          string | ||||||
|  | 	LastMessageTime string | ||||||
|  | 	IsMuted         string | ||||||
|  | 	IsMarkedSpam    string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newStore() *Store { | ||||||
|  | 	return &Store{ | ||||||
|  | 		make(map[string]Contact), | ||||||
|  | 		make(map[string]Chat), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) updateContacts(contacts interface{}) { | ||||||
|  | 	c, ok := contacts.([]interface{}) | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, contact := range c { | ||||||
|  | 		contactNode, ok := contact.(binary.Node) | ||||||
|  | 		if !ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1) | ||||||
|  | 		wac.Store.Contacts[jid] = Contact{ | ||||||
|  | 			jid, | ||||||
|  | 			contactNode.Attributes["notify"], | ||||||
|  | 			contactNode.Attributes["name"], | ||||||
|  | 			contactNode.Attributes["short"], | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) updateChats(chats interface{}) { | ||||||
|  | 	c, ok := chats.([]interface{}) | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, chat := range c { | ||||||
|  | 		chatNode, ok := chat.(binary.Node) | ||||||
|  | 		if !ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1) | ||||||
|  | 		wac.Store.Chats[jid] = Chat{ | ||||||
|  | 			jid, | ||||||
|  | 			chatNode.Attributes["name"], | ||||||
|  | 			chatNode.Attributes["count"], | ||||||
|  | 			chatNode.Attributes["t"], | ||||||
|  | 			chatNode.Attributes["mute"], | ||||||
|  | 			chatNode.Attributes["spam"], | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										125
									
								
								vendor/github.com/Rhymen/go-whatsapp/write.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/github.com/Rhymen/go-whatsapp/write.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | package whatsapp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/hmac" | ||||||
|  | 	"crypto/sha256" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/binary" | ||||||
|  | 	"github.com/Rhymen/go-whatsapp/crypto/cbc" | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | //writeJson enqueues a json message into the writeChan | ||||||
|  | func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) { | ||||||
|  | 	d, err := json.Marshal(data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ts := time.Now().Unix() | ||||||
|  | 	messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount) | ||||||
|  | 	bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d)) | ||||||
|  |  | ||||||
|  | 	ch, err := wac.write(websocket.TextMessage, messageTag, bytes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wac.msgCount++ | ||||||
|  | 	return ch, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) { | ||||||
|  | 	if len(messageTag) < 2 { | ||||||
|  | 		return nil, ErrMissingMessageTag | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	data, err := wac.encryptBinaryMessage(node) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "encryptBinaryMessage(node) failed") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	bytes := []byte(messageTag + ",") | ||||||
|  | 	bytes = append(bytes, byte(metric), byte(flag)) | ||||||
|  | 	bytes = append(bytes, data...) | ||||||
|  |  | ||||||
|  | 	ch, err := wac.write(websocket.BinaryMessage, messageTag, bytes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "failed to write message") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wac.msgCount++ | ||||||
|  | 	return ch, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) sendKeepAlive() error { | ||||||
|  | 	bytes := []byte("?,,") | ||||||
|  | 	respChan, err := wac.write(websocket.TextMessage, "!", bytes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.Wrap(err, "error sending keepAlive") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case resp := <-respChan: | ||||||
|  | 		msecs, err := strconv.ParseInt(resp, 10, 64) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "Error converting time string to uint") | ||||||
|  | 		} | ||||||
|  | 		wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond)) | ||||||
|  |  | ||||||
|  | 	case <-time.After(wac.msgTimeout): | ||||||
|  | 		return ErrConnectionTimeout | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) { | ||||||
|  | 	var ch chan string | ||||||
|  | 	if answerMessageTag != "" { | ||||||
|  | 		ch = make(chan string, 1) | ||||||
|  |  | ||||||
|  | 		wac.listener.Lock() | ||||||
|  | 		wac.listener.m[answerMessageTag] = ch | ||||||
|  | 		wac.listener.Unlock() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wac.ws.Lock() | ||||||
|  | 	err := wac.ws.conn.WriteMessage(messageType, data) | ||||||
|  | 	wac.ws.Unlock() | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		if answerMessageTag != "" { | ||||||
|  | 			wac.listener.Lock() | ||||||
|  | 			delete(wac.listener.m, answerMessageTag) | ||||||
|  | 			wac.listener.Unlock() | ||||||
|  | 		} | ||||||
|  | 		return nil, errors.Wrap(err, "error writing to websocket") | ||||||
|  | 	} | ||||||
|  | 	return ch, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) { | ||||||
|  | 	b, err := binary.Marshal(node) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "binary node marshal failed") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, errors.Wrap(err, "encrypt failed") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	h := hmac.New(sha256.New, wac.session.MacKey) | ||||||
|  | 	h.Write(cipher) | ||||||
|  | 	hash := h.Sum(nil) | ||||||
|  |  | ||||||
|  | 	data = append(data, hash[:32]...) | ||||||
|  | 	data = append(data, cipher...) | ||||||
|  |  | ||||||
|  | 	return data, nil | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								vendor/github.com/d5/tengo/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/d5/tengo/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,10 +1,13 @@ | |||||||
| vet: | vet: | ||||||
| 	go vet ./... | 	go vet ./... | ||||||
|  |  | ||||||
|  | generate: | ||||||
|  | 	go generate ./... | ||||||
|  |  | ||||||
| lint: | lint: | ||||||
| 	golint -set_exit_status ./... | 	golint -set_exit_status ./... | ||||||
|  |  | ||||||
| test: vet lint | test: generate vet lint | ||||||
| 	go test -race -cover ./... | 	go test -race -cover ./... | ||||||
|  |  | ||||||
| fmt: | fmt: | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								vendor/github.com/d5/tengo/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/d5/tengo/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,7 +7,7 @@ | |||||||
| [](https://godoc.org/github.com/d5/tengo/script) | [](https://godoc.org/github.com/d5/tengo/script) | ||||||
| [](https://goreportcard.com/report/github.com/d5/tengo) | [](https://goreportcard.com/report/github.com/d5/tengo) | ||||||
| [](https://travis-ci.org/d5/tengo) | [](https://travis-ci.org/d5/tengo) | ||||||
| [](https://www.patreon.com/tengolang) | [](https://sourcegraph.com/github.com/d5/tengo?badge) | ||||||
|  |  | ||||||
| **Tengo is a small, dynamic, fast, secure script language for Go.**  | **Tengo is a small, dynamic, fast, secure script language for Go.**  | ||||||
|  |  | ||||||
| @@ -16,6 +16,8 @@ Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as byt | |||||||
| ```golang | ```golang | ||||||
| /* The Tengo Language */ | /* The Tengo Language */ | ||||||
|  |  | ||||||
|  | fmt := import("fmt") | ||||||
|  |  | ||||||
| each := func(seq, fn) { | each := func(seq, fn) { | ||||||
|     for x in seq { fn(x) } |     for x in seq { fn(x) } | ||||||
| } | } | ||||||
| @@ -25,11 +27,11 @@ sum := func(init, seq) { | |||||||
|     return init |     return init | ||||||
| } | } | ||||||
|  |  | ||||||
| n := sum(0, [1, 2, 3])   // == 6 | fmt.println(sum(0, [1, 2, 3]))   // "6" | ||||||
| s := sum("", [1, 2, 3])  // == "123" | fmt.println(sum("", [1, 2, 3]))  // "123" | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| > Run this code in the [Playground](https://tengolang.com/?s=d01cf9ed81daba939e26618530eb171f7397d9c9) | > Run this code in the [Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3) | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
|  |  | ||||||
| @@ -41,22 +43,23 @@ s := sum("", [1, 2, 3])  // == "123" | |||||||
| - [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md) | - [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md) | ||||||
| - Compiler/runtime written in native Go _(no external deps or cgo)_ | - Compiler/runtime written in native Go _(no external deps or cgo)_ | ||||||
| - Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL | - Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL | ||||||
|  | - Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), [gaming](https://github.com/d5/pbr), data pipeline, [transpiler](https://github.com/d5/tengo2lua) | ||||||
|  |  | ||||||
| ## Benchmark | ## Benchmark | ||||||
|  |  | ||||||
| | | fib(35) | fibt(35) |  Type  | | | | fib(35) | fibt(35) |  Type  | | ||||||
| | :--- |    ---: |     ---: |  :---: | | | :--- |    ---: |     ---: |  :---: | | ||||||
| | Go | `58ms` | `4ms` | Go (native) | | | Go | `48ms` | `3ms` | Go (native) | | ||||||
| | [**Tengo**](https://github.com/d5/tengo) | `4,180ms` | `5ms` | VM on Go | | | [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go | | ||||||
| | Lua | `1,695ms` | `3ms` | Lua (native) | | | Lua | `1,416ms` | `3ms` | Lua (native) | | ||||||
| | [go-lua](https://github.com/Shopify/go-lua) | `5,163ms` | `5ms` | Lua VM on Go | | | [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go | | ||||||
| | [GopherLua](https://github.com/yuin/gopher-lua) | `5,525ms` | `5ms` | Lua VM on Go | | | [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go | | ||||||
| | Python | `3,097ms` | `27ms` | Python (native) | | | Python | `2,588ms` | `26ms` | Python (native) | | ||||||
| | [starlark-go](https://github.com/google/starlark-go) | `15,307ms` | `5ms` | Python-like Interpreter on Go | | | [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go | | ||||||
| | [gpython](https://github.com/go-python/gpython) | `17,656ms` | `5ms` | Python Interpreter on Go | | | [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go | | ||||||
| | [goja](https://github.com/dop251/goja) | `6,876ms` | `5ms` | JS VM on Go | | | [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go | | ||||||
| | [otto](https://github.com/robertkrimen/otto) | `81,886ms` | `12ms` | JS Interpreter on Go | | | [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go | | ||||||
| | [Anko](https://github.com/mattn/anko) | `97,517ms` | `14ms` | Interpreter on Go | | | [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go | | ||||||
|  |  | ||||||
| _* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_   | _* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_   | ||||||
| _* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_   | _* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_   | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								vendor/github.com/d5/tengo/compiler/ast/ident_list.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/d5/tengo/compiler/ast/ident_list.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -8,9 +8,10 @@ import ( | |||||||
|  |  | ||||||
| // IdentList represents a list of identifiers. | // IdentList represents a list of identifiers. | ||||||
| type IdentList struct { | type IdentList struct { | ||||||
| 	LParen source.Pos | 	LParen  source.Pos | ||||||
| 	List   []*Ident | 	VarArgs bool | ||||||
| 	RParen source.Pos | 	List    []*Ident | ||||||
|  | 	RParen  source.Pos | ||||||
| } | } | ||||||
|  |  | ||||||
| // Pos returns the position of first character belonging to the node. | // Pos returns the position of first character belonging to the node. | ||||||
| @@ -50,8 +51,12 @@ func (n *IdentList) NumFields() int { | |||||||
|  |  | ||||||
| func (n *IdentList) String() string { | func (n *IdentList) String() string { | ||||||
| 	var list []string | 	var list []string | ||||||
| 	for _, e := range n.List { | 	for i, e := range n.List { | ||||||
| 		list = append(list, e.String()) | 		if n.VarArgs && i == len(n.List)-1 { | ||||||
|  | 			list = append(list, "..."+e.String()) | ||||||
|  | 		} else { | ||||||
|  | 			list = append(list, e.String()) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return "(" + strings.Join(list, ", ") + ")" | 	return "(" + strings.Join(list, ", ") + ")" | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								vendor/github.com/d5/tengo/compiler/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/d5/tengo/compiler/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -17,32 +17,6 @@ type Bytecode struct { | |||||||
| 	Constants    []objects.Object | 	Constants    []objects.Object | ||||||
| } | } | ||||||
|  |  | ||||||
| // Decode reads Bytecode data from the reader. |  | ||||||
| func (b *Bytecode) Decode(r io.Reader) error { |  | ||||||
| 	dec := gob.NewDecoder(r) |  | ||||||
|  |  | ||||||
| 	if err := dec.Decode(&b.FileSet); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	// TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet |  | ||||||
| 	// as it's private field and not serialized by gob encoder/decoder. |  | ||||||
|  |  | ||||||
| 	if err := dec.Decode(&b.MainFunction); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := dec.Decode(&b.Constants); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// replace Bool and Undefined with known value |  | ||||||
| 	for i, v := range b.Constants { |  | ||||||
| 		b.Constants[i] = cleanupObjects(v) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Encode writes Bytecode data to the writer. | // Encode writes Bytecode data to the writer. | ||||||
| func (b *Bytecode) Encode(w io.Writer) error { | func (b *Bytecode) Encode(w io.Writer) error { | ||||||
| 	enc := gob.NewEncoder(w) | 	enc := gob.NewEncoder(w) | ||||||
| @@ -59,6 +33,17 @@ func (b *Bytecode) Encode(w io.Writer) error { | |||||||
| 	return enc.Encode(b.Constants) | 	return enc.Encode(b.Constants) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // CountObjects returns the number of objects found in Constants. | ||||||
|  | func (b *Bytecode) CountObjects() int { | ||||||
|  | 	n := 0 | ||||||
|  |  | ||||||
|  | 	for _, c := range b.Constants { | ||||||
|  | 		n += objects.CountObjects(c) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return n | ||||||
|  | } | ||||||
|  |  | ||||||
| // FormatInstructions returns human readable string representations of | // FormatInstructions returns human readable string representations of | ||||||
| // compiled instructions. | // compiled instructions. | ||||||
| func (b *Bytecode) FormatInstructions() []string { | func (b *Bytecode) FormatInstructions() []string { | ||||||
| @@ -83,51 +68,22 @@ func (b *Bytecode) FormatConstants() (output []string) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func cleanupObjects(o objects.Object) objects.Object { |  | ||||||
| 	switch o := o.(type) { |  | ||||||
| 	case *objects.Bool: |  | ||||||
| 		if o.IsFalsy() { |  | ||||||
| 			return objects.FalseValue |  | ||||||
| 		} |  | ||||||
| 		return objects.TrueValue |  | ||||||
| 	case *objects.Undefined: |  | ||||||
| 		return objects.UndefinedValue |  | ||||||
| 	case *objects.Array: |  | ||||||
| 		for i, v := range o.Value { |  | ||||||
| 			o.Value[i] = cleanupObjects(v) |  | ||||||
| 		} |  | ||||||
| 	case *objects.Map: |  | ||||||
| 		for k, v := range o.Value { |  | ||||||
| 			o.Value[k] = cleanupObjects(v) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return o |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	gob.Register(&source.FileSet{}) | 	gob.Register(&source.FileSet{}) | ||||||
| 	gob.Register(&source.File{}) | 	gob.Register(&source.File{}) | ||||||
| 	gob.Register(&objects.Array{}) | 	gob.Register(&objects.Array{}) | ||||||
| 	gob.Register(&objects.ArrayIterator{}) |  | ||||||
| 	gob.Register(&objects.Bool{}) | 	gob.Register(&objects.Bool{}) | ||||||
| 	gob.Register(&objects.Break{}) |  | ||||||
| 	gob.Register(&objects.BuiltinFunction{}) |  | ||||||
| 	gob.Register(&objects.Bytes{}) | 	gob.Register(&objects.Bytes{}) | ||||||
| 	gob.Register(&objects.Char{}) | 	gob.Register(&objects.Char{}) | ||||||
| 	gob.Register(&objects.Closure{}) | 	gob.Register(&objects.Closure{}) | ||||||
| 	gob.Register(&objects.CompiledFunction{}) | 	gob.Register(&objects.CompiledFunction{}) | ||||||
| 	gob.Register(&objects.Continue{}) |  | ||||||
| 	gob.Register(&objects.Error{}) | 	gob.Register(&objects.Error{}) | ||||||
| 	gob.Register(&objects.Float{}) | 	gob.Register(&objects.Float{}) | ||||||
| 	gob.Register(&objects.ImmutableArray{}) | 	gob.Register(&objects.ImmutableArray{}) | ||||||
| 	gob.Register(&objects.ImmutableMap{}) | 	gob.Register(&objects.ImmutableMap{}) | ||||||
| 	gob.Register(&objects.Int{}) | 	gob.Register(&objects.Int{}) | ||||||
| 	gob.Register(&objects.Map{}) | 	gob.Register(&objects.Map{}) | ||||||
| 	gob.Register(&objects.MapIterator{}) |  | ||||||
| 	gob.Register(&objects.ReturnValue{}) |  | ||||||
| 	gob.Register(&objects.String{}) | 	gob.Register(&objects.String{}) | ||||||
| 	gob.Register(&objects.StringIterator{}) |  | ||||||
| 	gob.Register(&objects.Time{}) | 	gob.Register(&objects.Time{}) | ||||||
| 	gob.Register(&objects.Undefined{}) | 	gob.Register(&objects.Undefined{}) | ||||||
| 	gob.Register(&objects.UserFunction{}) | 	gob.Register(&objects.UserFunction{}) | ||||||
|   | |||||||
							
								
								
									
										97
									
								
								vendor/github.com/d5/tengo/compiler/bytecode_decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/d5/tengo/compiler/bytecode_decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | package compiler | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/gob" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  |  | ||||||
|  | 	"github.com/d5/tengo/objects" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Decode reads Bytecode data from the reader. | ||||||
|  | func (b *Bytecode) Decode(r io.Reader, modules *objects.ModuleMap) error { | ||||||
|  | 	if modules == nil { | ||||||
|  | 		modules = objects.NewModuleMap() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dec := gob.NewDecoder(r) | ||||||
|  |  | ||||||
|  | 	if err := dec.Decode(&b.FileSet); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet | ||||||
|  | 	// as it's private field and not serialized by gob encoder/decoder. | ||||||
|  |  | ||||||
|  | 	if err := dec.Decode(&b.MainFunction); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := dec.Decode(&b.Constants); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	for i, v := range b.Constants { | ||||||
|  | 		fv, err := fixDecoded(v, modules) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		b.Constants[i] = fv | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func fixDecoded(o objects.Object, modules *objects.ModuleMap) (objects.Object, error) { | ||||||
|  | 	switch o := o.(type) { | ||||||
|  | 	case *objects.Bool: | ||||||
|  | 		if o.IsFalsy() { | ||||||
|  | 			return objects.FalseValue, nil | ||||||
|  | 		} | ||||||
|  | 		return objects.TrueValue, nil | ||||||
|  | 	case *objects.Undefined: | ||||||
|  | 		return objects.UndefinedValue, nil | ||||||
|  | 	case *objects.Array: | ||||||
|  | 		for i, v := range o.Value { | ||||||
|  | 			fv, err := fixDecoded(v, modules) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			o.Value[i] = fv | ||||||
|  | 		} | ||||||
|  | 	case *objects.ImmutableArray: | ||||||
|  | 		for i, v := range o.Value { | ||||||
|  | 			fv, err := fixDecoded(v, modules) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			o.Value[i] = fv | ||||||
|  | 		} | ||||||
|  | 	case *objects.Map: | ||||||
|  | 		for k, v := range o.Value { | ||||||
|  | 			fv, err := fixDecoded(v, modules) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			o.Value[k] = fv | ||||||
|  | 		} | ||||||
|  | 	case *objects.ImmutableMap: | ||||||
|  | 		modName := moduleName(o) | ||||||
|  | 		if mod := modules.GetBuiltinModule(modName); mod != nil { | ||||||
|  | 			return mod.AsImmutableMap(modName), nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for k, v := range o.Value { | ||||||
|  | 			// encoding of user function not supported | ||||||
|  | 			if _, isUserFunction := v.(*objects.UserFunction); isUserFunction { | ||||||
|  | 				return nil, fmt.Errorf("user function not decodable") | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			fv, err := fixDecoded(v, modules) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			o.Value[k] = fv | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return o, nil | ||||||
|  | } | ||||||
							
								
								
									
										129
									
								
								vendor/github.com/d5/tengo/compiler/bytecode_optimize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								vendor/github.com/d5/tengo/compiler/bytecode_optimize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | package compiler | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/d5/tengo/objects" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // RemoveDuplicates finds and remove the duplicate values in Constants. | ||||||
|  | // Note this function mutates Bytecode. | ||||||
|  | func (b *Bytecode) RemoveDuplicates() { | ||||||
|  | 	var deduped []objects.Object | ||||||
|  |  | ||||||
|  | 	indexMap := make(map[int]int) // mapping from old constant index to new index | ||||||
|  | 	ints := make(map[int64]int) | ||||||
|  | 	strings := make(map[string]int) | ||||||
|  | 	floats := make(map[float64]int) | ||||||
|  | 	chars := make(map[rune]int) | ||||||
|  | 	immutableMaps := make(map[string]int) // for modules | ||||||
|  |  | ||||||
|  | 	for curIdx, c := range b.Constants { | ||||||
|  | 		switch c := c.(type) { | ||||||
|  | 		case *objects.CompiledFunction: | ||||||
|  | 			// add to deduped list | ||||||
|  | 			indexMap[curIdx] = len(deduped) | ||||||
|  | 			deduped = append(deduped, c) | ||||||
|  | 		case *objects.ImmutableMap: | ||||||
|  | 			modName := moduleName(c) | ||||||
|  | 			newIdx, ok := immutableMaps[modName] | ||||||
|  | 			if modName != "" && ok { | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 			} else { | ||||||
|  | 				newIdx = len(deduped) | ||||||
|  | 				immutableMaps[modName] = newIdx | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 				deduped = append(deduped, c) | ||||||
|  | 			} | ||||||
|  | 		case *objects.Int: | ||||||
|  | 			if newIdx, ok := ints[c.Value]; ok { | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 			} else { | ||||||
|  | 				newIdx = len(deduped) | ||||||
|  | 				ints[c.Value] = newIdx | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 				deduped = append(deduped, c) | ||||||
|  | 			} | ||||||
|  | 		case *objects.String: | ||||||
|  | 			if newIdx, ok := strings[c.Value]; ok { | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 			} else { | ||||||
|  | 				newIdx = len(deduped) | ||||||
|  | 				strings[c.Value] = newIdx | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 				deduped = append(deduped, c) | ||||||
|  | 			} | ||||||
|  | 		case *objects.Float: | ||||||
|  | 			if newIdx, ok := floats[c.Value]; ok { | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 			} else { | ||||||
|  | 				newIdx = len(deduped) | ||||||
|  | 				floats[c.Value] = newIdx | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 				deduped = append(deduped, c) | ||||||
|  | 			} | ||||||
|  | 		case *objects.Char: | ||||||
|  | 			if newIdx, ok := chars[c.Value]; ok { | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 			} else { | ||||||
|  | 				newIdx = len(deduped) | ||||||
|  | 				chars[c.Value] = newIdx | ||||||
|  | 				indexMap[curIdx] = newIdx | ||||||
|  | 				deduped = append(deduped, c) | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName())) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// replace with de-duplicated constants | ||||||
|  | 	b.Constants = deduped | ||||||
|  |  | ||||||
|  | 	// update CONST instructions with new indexes | ||||||
|  | 	// main function | ||||||
|  | 	updateConstIndexes(b.MainFunction.Instructions, indexMap) | ||||||
|  | 	// other compiled functions in constants | ||||||
|  | 	for _, c := range b.Constants { | ||||||
|  | 		switch c := c.(type) { | ||||||
|  | 		case *objects.CompiledFunction: | ||||||
|  | 			updateConstIndexes(c.Instructions, indexMap) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func updateConstIndexes(insts []byte, indexMap map[int]int) { | ||||||
|  | 	i := 0 | ||||||
|  | 	for i < len(insts) { | ||||||
|  | 		op := insts[i] | ||||||
|  | 		numOperands := OpcodeOperands[op] | ||||||
|  | 		_, read := ReadOperands(numOperands, insts[i+1:]) | ||||||
|  |  | ||||||
|  | 		switch op { | ||||||
|  | 		case OpConstant: | ||||||
|  | 			curIdx := int(insts[i+2]) | int(insts[i+1])<<8 | ||||||
|  | 			newIdx, ok := indexMap[curIdx] | ||||||
|  | 			if !ok { | ||||||
|  | 				panic(fmt.Errorf("constant index not found: %d", curIdx)) | ||||||
|  | 			} | ||||||
|  | 			copy(insts[i:], MakeInstruction(op, newIdx)) | ||||||
|  | 		case OpClosure: | ||||||
|  | 			curIdx := int(insts[i+2]) | int(insts[i+1])<<8 | ||||||
|  | 			numFree := int(insts[i+3]) | ||||||
|  | 			newIdx, ok := indexMap[curIdx] | ||||||
|  | 			if !ok { | ||||||
|  | 				panic(fmt.Errorf("constant index not found: %d", curIdx)) | ||||||
|  | 			} | ||||||
|  | 			copy(insts[i:], MakeInstruction(op, newIdx, numFree)) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		i += 1 + read | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func moduleName(mod *objects.ImmutableMap) string { | ||||||
|  | 	if modName, ok := mod.Value["__module_name__"].(*objects.String); ok { | ||||||
|  | 		return modName.Value | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								vendor/github.com/d5/tengo/compiler/compilation_scope.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/d5/tengo/compiler/compilation_scope.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,8 +5,7 @@ import "github.com/d5/tengo/compiler/source" | |||||||
| // CompilationScope represents a compiled instructions | // CompilationScope represents a compiled instructions | ||||||
| // and the last two instructions that were emitted. | // and the last two instructions that were emitted. | ||||||
| type CompilationScope struct { | type CompilationScope struct { | ||||||
| 	instructions     []byte | 	instructions []byte | ||||||
| 	lastInstructions [2]EmittedInstruction | 	symbolInit   map[string]bool | ||||||
| 	symbolInit       map[string]bool | 	sourceMap    map[int]source.Pos | ||||||
| 	sourceMap        map[int]source.Pos |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										271
									
								
								vendor/github.com/d5/tengo/compiler/compiler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										271
									
								
								vendor/github.com/d5/tengo/compiler/compiler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,7 +3,10 @@ package compiler | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"path/filepath" | ||||||
| 	"reflect" | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/d5/tengo" | 	"github.com/d5/tengo" | ||||||
| 	"github.com/d5/tengo/compiler/ast" | 	"github.com/d5/tengo/compiler/ast" | ||||||
| @@ -16,14 +19,14 @@ import ( | |||||||
| type Compiler struct { | type Compiler struct { | ||||||
| 	file            *source.File | 	file            *source.File | ||||||
| 	parent          *Compiler | 	parent          *Compiler | ||||||
| 	moduleName      string | 	modulePath      string | ||||||
| 	constants       []objects.Object | 	constants       []objects.Object | ||||||
| 	symbolTable     *SymbolTable | 	symbolTable     *SymbolTable | ||||||
| 	scopes          []CompilationScope | 	scopes          []CompilationScope | ||||||
| 	scopeIndex      int | 	scopeIndex      int | ||||||
| 	moduleLoader    ModuleLoader | 	modules         *objects.ModuleMap | ||||||
| 	builtinModules  map[string]bool |  | ||||||
| 	compiledModules map[string]*objects.CompiledFunction | 	compiledModules map[string]*objects.CompiledFunction | ||||||
|  | 	allowFileImport bool | ||||||
| 	loops           []*Loop | 	loops           []*Loop | ||||||
| 	loopIndex       int | 	loopIndex       int | ||||||
| 	trace           io.Writer | 	trace           io.Writer | ||||||
| @@ -31,12 +34,7 @@ type Compiler struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // NewCompiler creates a Compiler. | // NewCompiler creates a Compiler. | ||||||
| // User can optionally provide the symbol table if one wants to add or remove | func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, modules *objects.ModuleMap, trace io.Writer) *Compiler { | ||||||
| // some global- or builtin- scope symbols. If not (nil), Compile will create |  | ||||||
| // a new symbol table and use the default builtin functions. Likewise, standard |  | ||||||
| // modules can be explicitly provided if user wants to add or remove some modules. |  | ||||||
| // By default, Compile will use all the standard modules otherwise. |  | ||||||
| func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, builtinModules map[string]bool, trace io.Writer) *Compiler { |  | ||||||
| 	mainScope := CompilationScope{ | 	mainScope := CompilationScope{ | ||||||
| 		symbolInit: make(map[string]bool), | 		symbolInit: make(map[string]bool), | ||||||
| 		sourceMap:  make(map[int]source.Pos), | 		sourceMap:  make(map[int]source.Pos), | ||||||
| @@ -45,15 +43,16 @@ func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []object | |||||||
| 	// symbol table | 	// symbol table | ||||||
| 	if symbolTable == nil { | 	if symbolTable == nil { | ||||||
| 		symbolTable = NewSymbolTable() | 		symbolTable = NewSymbolTable() | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 		for idx, fn := range objects.Builtins { | 	// add builtin functions to the symbol table | ||||||
| 			symbolTable.DefineBuiltin(idx, fn.Name) | 	for idx, fn := range objects.Builtins { | ||||||
| 		} | 		symbolTable.DefineBuiltin(idx, fn.Name) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// builtin modules | 	// builtin modules | ||||||
| 	if builtinModules == nil { | 	if modules == nil { | ||||||
| 		builtinModules = make(map[string]bool) | 		modules = objects.NewModuleMap() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return &Compiler{ | 	return &Compiler{ | ||||||
| @@ -64,7 +63,7 @@ func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []object | |||||||
| 		scopeIndex:      0, | 		scopeIndex:      0, | ||||||
| 		loopIndex:       -1, | 		loopIndex:       -1, | ||||||
| 		trace:           trace, | 		trace:           trace, | ||||||
| 		builtinModules:  builtinModules, | 		modules:         modules, | ||||||
| 		compiledModules: make(map[string]*objects.CompiledFunction), | 		compiledModules: make(map[string]*objects.CompiledFunction), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -120,7 +119,7 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			c.emit(node, OpGreaterThan) | 			c.emit(node, OpBinaryOp, int(token.Greater)) | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		} else if node.Token == token.LessEq { | 		} else if node.Token == token.LessEq { | ||||||
| @@ -131,7 +130,7 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			c.emit(node, OpGreaterThanEqual) | 			c.emit(node, OpBinaryOp, int(token.GreaterEq)) | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| @@ -145,35 +144,35 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
|  |  | ||||||
| 		switch node.Token { | 		switch node.Token { | ||||||
| 		case token.Add: | 		case token.Add: | ||||||
| 			c.emit(node, OpAdd) | 			c.emit(node, OpBinaryOp, int(token.Add)) | ||||||
| 		case token.Sub: | 		case token.Sub: | ||||||
| 			c.emit(node, OpSub) | 			c.emit(node, OpBinaryOp, int(token.Sub)) | ||||||
| 		case token.Mul: | 		case token.Mul: | ||||||
| 			c.emit(node, OpMul) | 			c.emit(node, OpBinaryOp, int(token.Mul)) | ||||||
| 		case token.Quo: | 		case token.Quo: | ||||||
| 			c.emit(node, OpDiv) | 			c.emit(node, OpBinaryOp, int(token.Quo)) | ||||||
| 		case token.Rem: | 		case token.Rem: | ||||||
| 			c.emit(node, OpRem) | 			c.emit(node, OpBinaryOp, int(token.Rem)) | ||||||
| 		case token.Greater: | 		case token.Greater: | ||||||
| 			c.emit(node, OpGreaterThan) | 			c.emit(node, OpBinaryOp, int(token.Greater)) | ||||||
| 		case token.GreaterEq: | 		case token.GreaterEq: | ||||||
| 			c.emit(node, OpGreaterThanEqual) | 			c.emit(node, OpBinaryOp, int(token.GreaterEq)) | ||||||
| 		case token.Equal: | 		case token.Equal: | ||||||
| 			c.emit(node, OpEqual) | 			c.emit(node, OpEqual) | ||||||
| 		case token.NotEqual: | 		case token.NotEqual: | ||||||
| 			c.emit(node, OpNotEqual) | 			c.emit(node, OpNotEqual) | ||||||
| 		case token.And: | 		case token.And: | ||||||
| 			c.emit(node, OpBAnd) | 			c.emit(node, OpBinaryOp, int(token.And)) | ||||||
| 		case token.Or: | 		case token.Or: | ||||||
| 			c.emit(node, OpBOr) | 			c.emit(node, OpBinaryOp, int(token.Or)) | ||||||
| 		case token.Xor: | 		case token.Xor: | ||||||
| 			c.emit(node, OpBXor) | 			c.emit(node, OpBinaryOp, int(token.Xor)) | ||||||
| 		case token.AndNot: | 		case token.AndNot: | ||||||
| 			c.emit(node, OpBAndNot) | 			c.emit(node, OpBinaryOp, int(token.AndNot)) | ||||||
| 		case token.Shl: | 		case token.Shl: | ||||||
| 			c.emit(node, OpBShiftLeft) | 			c.emit(node, OpBinaryOp, int(token.Shl)) | ||||||
| 		case token.Shr: | 		case token.Shr: | ||||||
| 			c.emit(node, OpBShiftRight) | 			c.emit(node, OpBinaryOp, int(token.Shr)) | ||||||
| 		default: | 		default: | ||||||
| 			return c.errorf(node, "invalid binary operator: %s", node.Token.String()) | 			return c.errorf(node, "invalid binary operator: %s", node.Token.String()) | ||||||
| 		} | 		} | ||||||
| @@ -293,6 +292,15 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case *ast.BlockStmt: | 	case *ast.BlockStmt: | ||||||
|  | 		if len(node.Stmts) == 0 { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		c.symbolTable = c.symbolTable.Fork(true) | ||||||
|  | 		defer func() { | ||||||
|  | 			c.symbolTable = c.symbolTable.Parent(false) | ||||||
|  | 		}() | ||||||
|  |  | ||||||
| 		for _, stmt := range node.Stmts { | 		for _, stmt := range node.Stmts { | ||||||
| 			if err := c.Compile(stmt); err != nil { | 			if err := c.Compile(stmt); err != nil { | ||||||
| 				return err | 				return err | ||||||
| @@ -405,10 +413,8 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// add OpReturn if function returns nothing | 		// code optimization | ||||||
| 		if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) { | 		c.optimizeFunc(node) | ||||||
| 			c.emit(node, OpReturn) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		freeSymbols := c.symbolTable.FreeSymbols() | 		freeSymbols := c.symbolTable.FreeSymbols() | ||||||
| 		numLocals := c.symbolTable.MaxSymbols() | 		numLocals := c.symbolTable.MaxSymbols() | ||||||
| @@ -461,9 +467,9 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 					s.LocalAssigned = true | 					s.LocalAssigned = true | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				c.emit(node, OpGetLocal, s.Index) | 				c.emit(node, OpGetLocalPtr, s.Index) | ||||||
| 			case ScopeFree: | 			case ScopeFree: | ||||||
| 				c.emit(node, OpGetFree, s.Index) | 				c.emit(node, OpGetFreePtr, s.Index) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -471,6 +477,7 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 			Instructions:  instructions, | 			Instructions:  instructions, | ||||||
| 			NumLocals:     numLocals, | 			NumLocals:     numLocals, | ||||||
| 			NumParameters: len(node.Type.Params.List), | 			NumParameters: len(node.Type.Params.List), | ||||||
|  | 			VarArgs:       node.Type.Params.VarArgs, | ||||||
| 			SourceMap:     sourceMap, | 			SourceMap:     sourceMap, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -487,13 +494,13 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if node.Result == nil { | 		if node.Result == nil { | ||||||
| 			c.emit(node, OpReturn) | 			c.emit(node, OpReturn, 0) | ||||||
| 		} else { | 		} else { | ||||||
| 			if err := c.Compile(node.Result); err != nil { | 			if err := c.Compile(node.Result); err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			c.emit(node, OpReturnValue) | 			c.emit(node, OpReturn, 1) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case *ast.CallExpr: | 	case *ast.CallExpr: | ||||||
| @@ -510,21 +517,57 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 		c.emit(node, OpCall, len(node.Args)) | 		c.emit(node, OpCall, len(node.Args)) | ||||||
|  |  | ||||||
| 	case *ast.ImportExpr: | 	case *ast.ImportExpr: | ||||||
| 		if c.builtinModules[node.ModuleName] { | 		if node.ModuleName == "" { | ||||||
| 			if len(node.ModuleName) > tengo.MaxStringLen { | 			return c.errorf(node, "empty module name") | ||||||
| 				return c.error(node, objects.ErrStringLimit) | 		} | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.ModuleName})) | 		if mod := c.modules.Get(node.ModuleName); mod != nil { | ||||||
| 			c.emit(node, OpGetBuiltinModule) | 			v, err := mod.Import(node.ModuleName) | ||||||
| 		} else { |  | ||||||
| 			userMod, err := c.compileModule(node) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			c.emit(node, OpConstant, c.addConstant(userMod)) | 			switch v := v.(type) { | ||||||
|  | 			case []byte: // module written in Tengo | ||||||
|  | 				compiled, err := c.compileModule(node, node.ModuleName, node.ModuleName, v) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 				c.emit(node, OpConstant, c.addConstant(compiled)) | ||||||
|  | 				c.emit(node, OpCall, 0) | ||||||
|  | 			case objects.Object: // builtin module | ||||||
|  | 				c.emit(node, OpConstant, c.addConstant(v)) | ||||||
|  | 			default: | ||||||
|  | 				panic(fmt.Errorf("invalid import value type: %T", v)) | ||||||
|  | 			} | ||||||
|  | 		} else if c.allowFileImport { | ||||||
|  | 			moduleName := node.ModuleName | ||||||
|  | 			if !strings.HasSuffix(moduleName, ".tengo") { | ||||||
|  | 				moduleName += ".tengo" | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			modulePath, err := filepath.Abs(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) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return c.errorf(node, "module file read error: %s", err.Error()) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			compiled, err := c.compileModule(node, moduleName, modulePath, moduleSrc) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			c.emit(node, OpConstant, c.addConstant(compiled)) | ||||||
| 			c.emit(node, OpCall, 0) | 			c.emit(node, OpCall, 0) | ||||||
|  | 		} else { | ||||||
|  | 			return c.errorf(node, "module '%s' not found", node.ModuleName) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case *ast.ExportStmt: | 	case *ast.ExportStmt: | ||||||
| @@ -543,7 +586,7 @@ func (c *Compiler) Compile(node ast.Node) error { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		c.emit(node, OpImmutable) | 		c.emit(node, OpImmutable) | ||||||
| 		c.emit(node, OpReturnValue) | 		c.emit(node, OpReturn, 1) | ||||||
|  |  | ||||||
| 	case *ast.ErrorExpr: | 	case *ast.ErrorExpr: | ||||||
| 		if err := c.Compile(node.Expr); err != nil { | 		if err := c.Compile(node.Expr); err != nil { | ||||||
| @@ -602,18 +645,16 @@ func (c *Compiler) Bytecode() *Bytecode { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetModuleLoader sets or replaces the current module loader. | // EnableFileImport enables or disables module loading from local files. | ||||||
| // Note that the module loader is used for user modules, | // Local file modules are disabled by default. | ||||||
| // not for the standard modules. | func (c *Compiler) EnableFileImport(enable bool) { | ||||||
| func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) { | 	c.allowFileImport = enable | ||||||
| 	c.moduleLoader = moduleLoader |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Compiler) fork(file *source.File, moduleName string, symbolTable *SymbolTable) *Compiler { | func (c *Compiler) fork(file *source.File, modulePath string, symbolTable *SymbolTable) *Compiler { | ||||||
| 	child := NewCompiler(file, symbolTable, nil, c.builtinModules, c.trace) | 	child := NewCompiler(file, symbolTable, nil, c.modules, c.trace) | ||||||
| 	child.moduleName = moduleName       // name of the module to compile | 	child.modulePath = modulePath // module file path | ||||||
| 	child.parent = c                    // parent to set to current compiler | 	child.parent = c              // parent to set to current compiler | ||||||
| 	child.moduleLoader = c.moduleLoader // share module loader |  | ||||||
|  |  | ||||||
| 	return child | 	return child | ||||||
| } | } | ||||||
| @@ -657,33 +698,6 @@ func (c *Compiler) addInstruction(b []byte) int { | |||||||
| 	return posNewIns | 	return posNewIns | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Compiler) setLastInstruction(op Opcode, pos int) { |  | ||||||
| 	c.scopes[c.scopeIndex].lastInstructions[1] = c.scopes[c.scopeIndex].lastInstructions[0] |  | ||||||
|  |  | ||||||
| 	c.scopes[c.scopeIndex].lastInstructions[0].Opcode = op |  | ||||||
| 	c.scopes[c.scopeIndex].lastInstructions[0].Position = pos |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Compiler) lastInstructionIs(op Opcode) bool { |  | ||||||
| 	if len(c.currentInstructions()) == 0 { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return c.scopes[c.scopeIndex].lastInstructions[0].Opcode == op |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Compiler) removeLastInstruction() { |  | ||||||
| 	lastPos := c.scopes[c.scopeIndex].lastInstructions[0].Position |  | ||||||
|  |  | ||||||
| 	if c.trace != nil { |  | ||||||
| 		c.printTrace(fmt.Sprintf("DELET %s", |  | ||||||
| 			FormatInstructions(c.scopes[c.scopeIndex].instructions[lastPos:], lastPos)[0])) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c.scopes[c.scopeIndex].instructions = c.currentInstructions()[:lastPos] |  | ||||||
| 	c.scopes[c.scopeIndex].lastInstructions[0] = c.scopes[c.scopeIndex].lastInstructions[1] |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Compiler) replaceInstruction(pos int, inst []byte) { | func (c *Compiler) replaceInstruction(pos int, inst []byte) { | ||||||
| 	copy(c.currentInstructions()[pos:], inst) | 	copy(c.currentInstructions()[pos:], inst) | ||||||
|  |  | ||||||
| @@ -700,6 +714,92 @@ func (c *Compiler) changeOperand(opPos int, operand ...int) { | |||||||
| 	c.replaceInstruction(opPos, inst) | 	c.replaceInstruction(opPos, inst) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // optimizeFunc performs some code-level optimization for the current function instructions | ||||||
|  | // it removes unreachable (dead code) instructions and adds "returns" instruction if needed. | ||||||
|  | func (c *Compiler) optimizeFunc(node ast.Node) { | ||||||
|  | 	// any instructions between RETURN and the function end | ||||||
|  | 	// or instructions between RETURN and jump target position | ||||||
|  | 	// are considered as unreachable. | ||||||
|  |  | ||||||
|  | 	// pass 1. identify all jump destinations | ||||||
|  | 	dsts := make(map[int]bool) | ||||||
|  | 	iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool { | ||||||
|  | 		switch opcode { | ||||||
|  | 		case OpJump, OpJumpFalsy, OpAndJump, OpOrJump: | ||||||
|  | 			dsts[operands[0]] = true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	var newInsts []byte | ||||||
|  |  | ||||||
|  | 	// pass 2. eliminate dead code | ||||||
|  | 	posMap := make(map[int]int) // old position to new position | ||||||
|  | 	var dstIdx int | ||||||
|  | 	var deadCode bool | ||||||
|  | 	iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool { | ||||||
|  | 		switch { | ||||||
|  | 		case opcode == OpReturn: | ||||||
|  | 			if deadCode { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 			deadCode = true | ||||||
|  | 		case dsts[pos]: | ||||||
|  | 			dstIdx++ | ||||||
|  | 			deadCode = false | ||||||
|  | 		case deadCode: | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		posMap[pos] = len(newInsts) | ||||||
|  | 		newInsts = append(newInsts, MakeInstruction(opcode, operands...)...) | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// pass 3. update jump positions | ||||||
|  | 	var lastOp Opcode | ||||||
|  | 	var appendReturn bool | ||||||
|  | 	endPos := len(c.scopes[c.scopeIndex].instructions) | ||||||
|  | 	iterateInstructions(newInsts, func(pos int, opcode Opcode, operands []int) bool { | ||||||
|  | 		switch opcode { | ||||||
|  | 		case OpJump, OpJumpFalsy, OpAndJump, OpOrJump: | ||||||
|  | 			newDst, ok := posMap[operands[0]] | ||||||
|  | 			if ok { | ||||||
|  | 				copy(newInsts[pos:], MakeInstruction(opcode, newDst)) | ||||||
|  | 			} else if endPos == operands[0] { | ||||||
|  | 				// there's a jump instruction that jumps to the end of function | ||||||
|  | 				// compiler should append "return". | ||||||
|  | 				appendReturn = true | ||||||
|  | 			} else { | ||||||
|  | 				panic(fmt.Errorf("invalid jump position: %d", newDst)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		lastOp = opcode | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
|  | 	if lastOp != OpReturn { | ||||||
|  | 		appendReturn = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// pass 4. update source map | ||||||
|  | 	newSourceMap := make(map[int]source.Pos) | ||||||
|  | 	for pos, srcPos := range c.scopes[c.scopeIndex].sourceMap { | ||||||
|  | 		newPos, ok := posMap[pos] | ||||||
|  | 		if ok { | ||||||
|  | 			newSourceMap[newPos] = srcPos | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c.scopes[c.scopeIndex].instructions = newInsts | ||||||
|  | 	c.scopes[c.scopeIndex].sourceMap = newSourceMap | ||||||
|  |  | ||||||
|  | 	// append "return" | ||||||
|  | 	if appendReturn { | ||||||
|  | 		c.emit(node, OpReturn, 0) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int { | func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int { | ||||||
| 	filePos := source.NoPos | 	filePos := source.NoPos | ||||||
| 	if node != nil { | 	if node != nil { | ||||||
| @@ -709,7 +809,6 @@ func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int { | |||||||
| 	inst := MakeInstruction(opcode, operands...) | 	inst := MakeInstruction(opcode, operands...) | ||||||
| 	pos := c.addInstruction(inst) | 	pos := c.addInstruction(inst) | ||||||
| 	c.scopes[c.scopeIndex].sourceMap[pos] = filePos | 	c.scopes[c.scopeIndex].sourceMap[pos] = filePos | ||||||
| 	c.setLastInstruction(opcode, pos) |  | ||||||
|  |  | ||||||
| 	if c.trace != nil { | 	if c.trace != nil { | ||||||
| 		c.printTrace(fmt.Sprintf("EMIT  %s", | 		c.printTrace(fmt.Sprintf("EMIT  %s", | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								vendor/github.com/d5/tengo/compiler/compiler_assign.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/d5/tengo/compiler/compiler_assign.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -51,27 +51,27 @@ func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.To | |||||||
|  |  | ||||||
| 	switch op { | 	switch op { | ||||||
| 	case token.AddAssign: | 	case token.AddAssign: | ||||||
| 		c.emit(node, OpAdd) | 		c.emit(node, OpBinaryOp, int(token.Add)) | ||||||
| 	case token.SubAssign: | 	case token.SubAssign: | ||||||
| 		c.emit(node, OpSub) | 		c.emit(node, OpBinaryOp, int(token.Sub)) | ||||||
| 	case token.MulAssign: | 	case token.MulAssign: | ||||||
| 		c.emit(node, OpMul) | 		c.emit(node, OpBinaryOp, int(token.Mul)) | ||||||
| 	case token.QuoAssign: | 	case token.QuoAssign: | ||||||
| 		c.emit(node, OpDiv) | 		c.emit(node, OpBinaryOp, int(token.Quo)) | ||||||
| 	case token.RemAssign: | 	case token.RemAssign: | ||||||
| 		c.emit(node, OpRem) | 		c.emit(node, OpBinaryOp, int(token.Rem)) | ||||||
| 	case token.AndAssign: | 	case token.AndAssign: | ||||||
| 		c.emit(node, OpBAnd) | 		c.emit(node, OpBinaryOp, int(token.And)) | ||||||
| 	case token.OrAssign: | 	case token.OrAssign: | ||||||
| 		c.emit(node, OpBOr) | 		c.emit(node, OpBinaryOp, int(token.Or)) | ||||||
| 	case token.AndNotAssign: | 	case token.AndNotAssign: | ||||||
| 		c.emit(node, OpBAndNot) | 		c.emit(node, OpBinaryOp, int(token.AndNot)) | ||||||
| 	case token.XorAssign: | 	case token.XorAssign: | ||||||
| 		c.emit(node, OpBXor) | 		c.emit(node, OpBinaryOp, int(token.Xor)) | ||||||
| 	case token.ShlAssign: | 	case token.ShlAssign: | ||||||
| 		c.emit(node, OpBShiftLeft) | 		c.emit(node, OpBinaryOp, int(token.Shl)) | ||||||
| 	case token.ShrAssign: | 	case token.ShrAssign: | ||||||
| 		c.emit(node, OpBShiftRight) | 		c.emit(node, OpBinaryOp, int(token.Shr)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// compile selector expressions (right to left) | 	// compile selector expressions (right to left) | ||||||
|   | |||||||
							
								
								
									
										91
									
								
								vendor/github.com/d5/tengo/compiler/compiler_module.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										91
									
								
								vendor/github.com/d5/tengo/compiler/compiler_module.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,72 +1,31 @@ | |||||||
| package compiler | package compiler | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"io/ioutil" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"github.com/d5/tengo/compiler/ast" | 	"github.com/d5/tengo/compiler/ast" | ||||||
| 	"github.com/d5/tengo/compiler/parser" | 	"github.com/d5/tengo/compiler/parser" | ||||||
| 	"github.com/d5/tengo/objects" | 	"github.com/d5/tengo/objects" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (c *Compiler) compileModule(expr *ast.ImportExpr) (*objects.CompiledFunction, error) { | func (c *Compiler) checkCyclicImports(node ast.Node, modulePath string) error { | ||||||
| 	compiledModule, exists := c.loadCompiledModule(expr.ModuleName) | 	if c.modulePath == modulePath { | ||||||
| 	if exists { | 		return c.errorf(node, "cyclic module import: %s", modulePath) | ||||||
| 		return compiledModule, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	moduleName := expr.ModuleName |  | ||||||
|  |  | ||||||
| 	// read module source from loader |  | ||||||
| 	var moduleSrc []byte |  | ||||||
| 	if c.moduleLoader == nil { |  | ||||||
| 		// default loader: read from local file |  | ||||||
| 		if !strings.HasSuffix(moduleName, ".tengo") { |  | ||||||
| 			moduleName += ".tengo" |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if err := c.checkCyclicImports(expr, moduleName); err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		var err error |  | ||||||
| 		moduleSrc, err = ioutil.ReadFile(moduleName) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, c.errorf(expr, "module file read error: %s", err.Error()) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		if err := c.checkCyclicImports(expr, moduleName); err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		var err error |  | ||||||
| 		moduleSrc, err = c.moduleLoader(moduleName) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	compiledModule, err := c.doCompileModule(moduleName, moduleSrc) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c.storeCompiledModule(moduleName, compiledModule) |  | ||||||
|  |  | ||||||
| 	return compiledModule, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *Compiler) checkCyclicImports(node ast.Node, moduleName string) error { |  | ||||||
| 	if c.moduleName == moduleName { |  | ||||||
| 		return c.errorf(node, "cyclic module import: %s", moduleName) |  | ||||||
| 	} else if c.parent != nil { | 	} else if c.parent != nil { | ||||||
| 		return c.parent.checkCyclicImports(node, moduleName) | 		return c.parent.checkCyclicImports(node, modulePath) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.CompiledFunction, error) { | func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, src []byte) (*objects.CompiledFunction, error) { | ||||||
|  | 	if err := c.checkCyclicImports(node, modulePath); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	compiledModule, exists := c.loadCompiledModule(modulePath) | ||||||
|  | 	if exists { | ||||||
|  | 		return compiledModule, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	modFile := c.file.Set().AddFile(moduleName, -1, len(src)) | 	modFile := c.file.Set().AddFile(moduleName, -1, len(src)) | ||||||
| 	p := parser.NewParser(modFile, src, nil) | 	p := parser.NewParser(modFile, src, nil) | ||||||
| 	file, err := p.ParseFile() | 	file, err := p.ParseFile() | ||||||
| @@ -85,36 +44,36 @@ func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.Comp | |||||||
| 	symbolTable = symbolTable.Fork(false) | 	symbolTable = symbolTable.Fork(false) | ||||||
|  |  | ||||||
| 	// compile module | 	// compile module | ||||||
| 	moduleCompiler := c.fork(modFile, moduleName, symbolTable) | 	moduleCompiler := c.fork(modFile, modulePath, symbolTable) | ||||||
| 	if err := moduleCompiler.Compile(file); err != nil { | 	if err := moduleCompiler.Compile(file); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// add OpReturn (== export undefined) if export is missing | 	// code optimization | ||||||
| 	if !moduleCompiler.lastInstructionIs(OpReturnValue) { | 	moduleCompiler.optimizeFunc(node) | ||||||
| 		moduleCompiler.emit(nil, OpReturn) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	compiledFunc := moduleCompiler.Bytecode().MainFunction | 	compiledFunc := moduleCompiler.Bytecode().MainFunction | ||||||
| 	compiledFunc.NumLocals = symbolTable.MaxSymbols() | 	compiledFunc.NumLocals = symbolTable.MaxSymbols() | ||||||
|  |  | ||||||
|  | 	c.storeCompiledModule(modulePath, compiledFunc) | ||||||
|  |  | ||||||
| 	return compiledFunc, nil | 	return compiledFunc, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Compiler) loadCompiledModule(moduleName string) (mod *objects.CompiledFunction, ok bool) { | func (c *Compiler) loadCompiledModule(modulePath string) (mod *objects.CompiledFunction, ok bool) { | ||||||
| 	if c.parent != nil { | 	if c.parent != nil { | ||||||
| 		return c.parent.loadCompiledModule(moduleName) | 		return c.parent.loadCompiledModule(modulePath) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	mod, ok = c.compiledModules[moduleName] | 	mod, ok = c.compiledModules[modulePath] | ||||||
|  |  | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Compiler) storeCompiledModule(moduleName string, module *objects.CompiledFunction) { | func (c *Compiler) storeCompiledModule(modulePath string, module *objects.CompiledFunction) { | ||||||
| 	if c.parent != nil { | 	if c.parent != nil { | ||||||
| 		c.parent.storeCompiledModule(moduleName, module) | 		c.parent.storeCompiledModule(modulePath, module) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c.compiledModules[moduleName] = module | 	c.compiledModules[modulePath] = module | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								vendor/github.com/d5/tengo/compiler/instructions.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/d5/tengo/compiler/instructions.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ func MakeInstruction(opcode Opcode, operands ...int) []byte { | |||||||
| 		totalLen += w | 		totalLen += w | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	instruction := make([]byte, totalLen, totalLen) | 	instruction := make([]byte, totalLen) | ||||||
| 	instruction[0] = byte(opcode) | 	instruction[0] = byte(opcode) | ||||||
|  |  | ||||||
| 	offset := 1 | 	offset := 1 | ||||||
| @@ -57,3 +57,16 @@ func FormatInstructions(b []byte, posOffset int) []string { | |||||||
|  |  | ||||||
| 	return out | 	return out | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func iterateInstructions(b []byte, fn func(pos int, opcode Opcode, operands []int) bool) { | ||||||
|  | 	for i := 0; i < len(b); i++ { | ||||||
|  | 		numOperands := OpcodeOperands[Opcode(b[i])] | ||||||
|  | 		operands, read := ReadOperands(numOperands, b[i+1:]) | ||||||
|  |  | ||||||
|  | 		if !fn(i, b[i], operands) { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		i += read | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										282
									
								
								vendor/github.com/d5/tengo/compiler/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										282
									
								
								vendor/github.com/d5/tengo/compiler/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,173 +5,137 @@ type Opcode = byte | |||||||
|  |  | ||||||
| // List of opcodes | // List of opcodes | ||||||
| const ( | const ( | ||||||
| 	OpConstant         Opcode = iota // Load constant | 	OpConstant      Opcode = iota // Load constant | ||||||
| 	OpAdd                            // Add | 	OpBComplement                 // bitwise complement | ||||||
| 	OpSub                            // Sub | 	OpPop                         // Pop | ||||||
| 	OpMul                            // Multiply | 	OpTrue                        // Push true | ||||||
| 	OpDiv                            // Divide | 	OpFalse                       // Push false | ||||||
| 	OpRem                            // Remainder | 	OpEqual                       // Equal == | ||||||
| 	OpBAnd                           // bitwise AND | 	OpNotEqual                    // Not equal != | ||||||
| 	OpBOr                            // bitwise OR | 	OpMinus                       // Minus - | ||||||
| 	OpBXor                           // bitwise XOR | 	OpLNot                        // Logical not ! | ||||||
| 	OpBShiftLeft                     // bitwise shift left | 	OpJumpFalsy                   // Jump if falsy | ||||||
| 	OpBShiftRight                    // bitwise shift right | 	OpAndJump                     // Logical AND jump | ||||||
| 	OpBAndNot                        // bitwise AND NOT | 	OpOrJump                      // Logical OR jump | ||||||
| 	OpBComplement                    // bitwise complement | 	OpJump                        // Jump | ||||||
| 	OpPop                            // Pop | 	OpNull                        // Push null | ||||||
| 	OpTrue                           // Push true | 	OpArray                       // Array object | ||||||
| 	OpFalse                          // Push false | 	OpMap                         // Map object | ||||||
| 	OpEqual                          // Equal == | 	OpError                       // Error object | ||||||
| 	OpNotEqual                       // Not equal != | 	OpImmutable                   // Immutable object | ||||||
| 	OpGreaterThan                    // Greater than >= | 	OpIndex                       // Index operation | ||||||
| 	OpGreaterThanEqual               // Greater than or equal to >= | 	OpSliceIndex                  // Slice operation | ||||||
| 	OpMinus                          // Minus - | 	OpCall                        // Call function | ||||||
| 	OpLNot                           // Logical not ! | 	OpReturn                      // Return | ||||||
| 	OpJumpFalsy                      // Jump if falsy | 	OpGetGlobal                   // Get global variable | ||||||
| 	OpAndJump                        // Logical AND jump | 	OpSetGlobal                   // Set global variable | ||||||
| 	OpOrJump                         // Logical OR jump | 	OpSetSelGlobal                // Set global variable using selectors | ||||||
| 	OpJump                           // Jump | 	OpGetLocal                    // Get local variable | ||||||
| 	OpNull                           // Push null | 	OpSetLocal                    // Set local variable | ||||||
| 	OpArray                          // Array object | 	OpDefineLocal                 // Define local variable | ||||||
| 	OpMap                            // Map object | 	OpSetSelLocal                 // Set local variable using selectors | ||||||
| 	OpError                          // Error object | 	OpGetFreePtr                  // Get free variable pointer object | ||||||
| 	OpImmutable                      // Immutable object | 	OpGetFree                     // Get free variables | ||||||
| 	OpIndex                          // Index operation | 	OpSetFree                     // Set free variables | ||||||
| 	OpSliceIndex                     // Slice operation | 	OpGetLocalPtr                 // Get local variable as a pointer | ||||||
| 	OpCall                           // Call function | 	OpSetSelFree                  // Set free variables using selectors | ||||||
| 	OpReturn                         // Return | 	OpGetBuiltin                  // Get builtin function | ||||||
| 	OpReturnValue                    // Return value | 	OpClosure                     // Push closure | ||||||
| 	OpGetGlobal                      // Get global variable | 	OpIteratorInit                // Iterator init | ||||||
| 	OpSetGlobal                      // Set global variable | 	OpIteratorNext                // Iterator next | ||||||
| 	OpSetSelGlobal                   // Set global variable using selectors | 	OpIteratorKey                 // Iterator key | ||||||
| 	OpGetLocal                       // Get local variable | 	OpIteratorValue               // Iterator value | ||||||
| 	OpSetLocal                       // Set local variable | 	OpBinaryOp                    // Binary Operation | ||||||
| 	OpDefineLocal                    // Define local variable |  | ||||||
| 	OpSetSelLocal                    // Set local variable using selectors |  | ||||||
| 	OpGetFree                        // Get free variables |  | ||||||
| 	OpSetFree                        // Set free variables |  | ||||||
| 	OpSetSelFree                     // Set free variables using selectors |  | ||||||
| 	OpGetBuiltin                     // Get builtin function |  | ||||||
| 	OpGetBuiltinModule               // Get builtin module |  | ||||||
| 	OpClosure                        // Push closure |  | ||||||
| 	OpIteratorInit                   // Iterator init |  | ||||||
| 	OpIteratorNext                   // Iterator next |  | ||||||
| 	OpIteratorKey                    // Iterator key |  | ||||||
| 	OpIteratorValue                  // Iterator value |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // OpcodeNames is opcode names. | // OpcodeNames is opcode names. | ||||||
| var OpcodeNames = [...]string{ | var OpcodeNames = [...]string{ | ||||||
| 	OpConstant:         "CONST", | 	OpConstant:      "CONST", | ||||||
| 	OpPop:              "POP", | 	OpPop:           "POP", | ||||||
| 	OpTrue:             "TRUE", | 	OpTrue:          "TRUE", | ||||||
| 	OpFalse:            "FALSE", | 	OpFalse:         "FALSE", | ||||||
| 	OpAdd:              "ADD", | 	OpBComplement:   "NEG", | ||||||
| 	OpSub:              "SUB", | 	OpEqual:         "EQL", | ||||||
| 	OpMul:              "MUL", | 	OpNotEqual:      "NEQ", | ||||||
| 	OpDiv:              "DIV", | 	OpMinus:         "NEG", | ||||||
| 	OpRem:              "REM", | 	OpLNot:          "NOT", | ||||||
| 	OpBAnd:             "AND", | 	OpJumpFalsy:     "JMPF", | ||||||
| 	OpBOr:              "OR", | 	OpAndJump:       "ANDJMP", | ||||||
| 	OpBXor:             "XOR", | 	OpOrJump:        "ORJMP", | ||||||
| 	OpBAndNot:          "ANDN", | 	OpJump:          "JMP", | ||||||
| 	OpBShiftLeft:       "SHL", | 	OpNull:          "NULL", | ||||||
| 	OpBShiftRight:      "SHR", | 	OpGetGlobal:     "GETG", | ||||||
| 	OpBComplement:      "NEG", | 	OpSetGlobal:     "SETG", | ||||||
| 	OpEqual:            "EQL", | 	OpSetSelGlobal:  "SETSG", | ||||||
| 	OpNotEqual:         "NEQ", | 	OpArray:         "ARR", | ||||||
| 	OpGreaterThan:      "GTR", | 	OpMap:           "MAP", | ||||||
| 	OpGreaterThanEqual: "GEQ", | 	OpError:         "ERROR", | ||||||
| 	OpMinus:            "NEG", | 	OpImmutable:     "IMMUT", | ||||||
| 	OpLNot:             "NOT", | 	OpIndex:         "INDEX", | ||||||
| 	OpJumpFalsy:        "JMPF", | 	OpSliceIndex:    "SLICE", | ||||||
| 	OpAndJump:          "ANDJMP", | 	OpCall:          "CALL", | ||||||
| 	OpOrJump:           "ORJMP", | 	OpReturn:        "RET", | ||||||
| 	OpJump:             "JMP", | 	OpGetLocal:      "GETL", | ||||||
| 	OpNull:             "NULL", | 	OpSetLocal:      "SETL", | ||||||
| 	OpGetGlobal:        "GETG", | 	OpDefineLocal:   "DEFL", | ||||||
| 	OpSetGlobal:        "SETG", | 	OpSetSelLocal:   "SETSL", | ||||||
| 	OpSetSelGlobal:     "SETSG", | 	OpGetBuiltin:    "BUILTIN", | ||||||
| 	OpArray:            "ARR", | 	OpClosure:       "CLOSURE", | ||||||
| 	OpMap:              "MAP", | 	OpGetFreePtr:    "GETFP", | ||||||
| 	OpError:            "ERROR", | 	OpGetFree:       "GETF", | ||||||
| 	OpImmutable:        "IMMUT", | 	OpSetFree:       "SETF", | ||||||
| 	OpIndex:            "INDEX", | 	OpGetLocalPtr:   "GETLP", | ||||||
| 	OpSliceIndex:       "SLICE", | 	OpSetSelFree:    "SETSF", | ||||||
| 	OpCall:             "CALL", | 	OpIteratorInit:  "ITER", | ||||||
| 	OpReturn:           "RET", | 	OpIteratorNext:  "ITNXT", | ||||||
| 	OpReturnValue:      "RETVAL", | 	OpIteratorKey:   "ITKEY", | ||||||
| 	OpGetLocal:         "GETL", | 	OpIteratorValue: "ITVAL", | ||||||
| 	OpSetLocal:         "SETL", | 	OpBinaryOp:      "BINARYOP", | ||||||
| 	OpDefineLocal:      "DEFL", |  | ||||||
| 	OpSetSelLocal:      "SETSL", |  | ||||||
| 	OpGetBuiltin:       "BUILTIN", |  | ||||||
| 	OpGetBuiltinModule: "BLTMOD", |  | ||||||
| 	OpClosure:          "CLOSURE", |  | ||||||
| 	OpGetFree:          "GETF", |  | ||||||
| 	OpSetFree:          "SETF", |  | ||||||
| 	OpSetSelFree:       "SETSF", |  | ||||||
| 	OpIteratorInit:     "ITER", |  | ||||||
| 	OpIteratorNext:     "ITNXT", |  | ||||||
| 	OpIteratorKey:      "ITKEY", |  | ||||||
| 	OpIteratorValue:    "ITVAL", |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // OpcodeOperands is the number of operands. | // OpcodeOperands is the number of operands. | ||||||
| var OpcodeOperands = [...][]int{ | var OpcodeOperands = [...][]int{ | ||||||
| 	OpConstant:         {2}, | 	OpConstant:      {2}, | ||||||
| 	OpPop:              {}, | 	OpPop:           {}, | ||||||
| 	OpTrue:             {}, | 	OpTrue:          {}, | ||||||
| 	OpFalse:            {}, | 	OpFalse:         {}, | ||||||
| 	OpAdd:              {}, | 	OpBComplement:   {}, | ||||||
| 	OpSub:              {}, | 	OpEqual:         {}, | ||||||
| 	OpMul:              {}, | 	OpNotEqual:      {}, | ||||||
| 	OpDiv:              {}, | 	OpMinus:         {}, | ||||||
| 	OpRem:              {}, | 	OpLNot:          {}, | ||||||
| 	OpBAnd:             {}, | 	OpJumpFalsy:     {2}, | ||||||
| 	OpBOr:              {}, | 	OpAndJump:       {2}, | ||||||
| 	OpBXor:             {}, | 	OpOrJump:        {2}, | ||||||
| 	OpBAndNot:          {}, | 	OpJump:          {2}, | ||||||
| 	OpBShiftLeft:       {}, | 	OpNull:          {}, | ||||||
| 	OpBShiftRight:      {}, | 	OpGetGlobal:     {2}, | ||||||
| 	OpBComplement:      {}, | 	OpSetGlobal:     {2}, | ||||||
| 	OpEqual:            {}, | 	OpSetSelGlobal:  {2, 1}, | ||||||
| 	OpNotEqual:         {}, | 	OpArray:         {2}, | ||||||
| 	OpGreaterThan:      {}, | 	OpMap:           {2}, | ||||||
| 	OpGreaterThanEqual: {}, | 	OpError:         {}, | ||||||
| 	OpMinus:            {}, | 	OpImmutable:     {}, | ||||||
| 	OpLNot:             {}, | 	OpIndex:         {}, | ||||||
| 	OpJumpFalsy:        {2}, | 	OpSliceIndex:    {}, | ||||||
| 	OpAndJump:          {2}, | 	OpCall:          {1}, | ||||||
| 	OpOrJump:           {2}, | 	OpReturn:        {1}, | ||||||
| 	OpJump:             {2}, | 	OpGetLocal:      {1}, | ||||||
| 	OpNull:             {}, | 	OpSetLocal:      {1}, | ||||||
| 	OpGetGlobal:        {2}, | 	OpDefineLocal:   {1}, | ||||||
| 	OpSetGlobal:        {2}, | 	OpSetSelLocal:   {1, 1}, | ||||||
| 	OpSetSelGlobal:     {2, 1}, | 	OpGetBuiltin:    {1}, | ||||||
| 	OpArray:            {2}, | 	OpClosure:       {2, 1}, | ||||||
| 	OpMap:              {2}, | 	OpGetFreePtr:    {1}, | ||||||
| 	OpError:            {}, | 	OpGetFree:       {1}, | ||||||
| 	OpImmutable:        {}, | 	OpSetFree:       {1}, | ||||||
| 	OpIndex:            {}, | 	OpGetLocalPtr:   {1}, | ||||||
| 	OpSliceIndex:       {}, | 	OpSetSelFree:    {1, 1}, | ||||||
| 	OpCall:             {1}, | 	OpIteratorInit:  {}, | ||||||
| 	OpReturn:           {}, | 	OpIteratorNext:  {}, | ||||||
| 	OpReturnValue:      {}, | 	OpIteratorKey:   {}, | ||||||
| 	OpGetLocal:         {1}, | 	OpIteratorValue: {}, | ||||||
| 	OpSetLocal:         {1}, | 	OpBinaryOp:      {1}, | ||||||
| 	OpDefineLocal:      {1}, |  | ||||||
| 	OpSetSelLocal:      {1, 1}, |  | ||||||
| 	OpGetBuiltin:       {1}, |  | ||||||
| 	OpGetBuiltinModule: {}, |  | ||||||
| 	OpClosure:          {2, 1}, |  | ||||||
| 	OpGetFree:          {1}, |  | ||||||
| 	OpSetFree:          {1}, |  | ||||||
| 	OpSetSelFree:       {1, 1}, |  | ||||||
| 	OpIteratorInit:     {}, |  | ||||||
| 	OpIteratorNext:     {}, |  | ||||||
| 	OpIteratorKey:      {}, |  | ||||||
| 	OpIteratorValue:    {}, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // ReadOperands reads operands from the bytecode. | // ReadOperands reads operands from the bytecode. | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								vendor/github.com/d5/tengo/compiler/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/d5/tengo/compiler/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -610,19 +610,31 @@ func (p *Parser) parseIdentList() *ast.IdentList { | |||||||
|  |  | ||||||
| 	var params []*ast.Ident | 	var params []*ast.Ident | ||||||
| 	lparen := p.expect(token.LParen) | 	lparen := p.expect(token.LParen) | ||||||
|  | 	isVarArgs := false | ||||||
| 	if p.token != token.RParen { | 	if p.token != token.RParen { | ||||||
| 		params = append(params, p.parseIdent()) | 		if p.token == token.Ellipsis { | ||||||
| 		for p.token == token.Comma { | 			isVarArgs = true | ||||||
| 			p.next() | 			p.next() | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		params = append(params, p.parseIdent()) | ||||||
|  | 		for !isVarArgs && p.token == token.Comma { | ||||||
|  | 			p.next() | ||||||
|  | 			if p.token == token.Ellipsis { | ||||||
|  | 				isVarArgs = true | ||||||
|  | 				p.next() | ||||||
|  | 			} | ||||||
| 			params = append(params, p.parseIdent()) | 			params = append(params, p.parseIdent()) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rparen := p.expect(token.RParen) | 	rparen := p.expect(token.RParen) | ||||||
|  |  | ||||||
| 	return &ast.IdentList{ | 	return &ast.IdentList{ | ||||||
| 		LParen: lparen, | 		LParen:  lparen, | ||||||
| 		RParen: rparen, | 		RParen:  rparen, | ||||||
| 		List:   params, | 		VarArgs: isVarArgs, | ||||||
|  | 		List:    params, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								vendor/github.com/d5/tengo/compiler/symbol_scopes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/d5/tengo/compiler/symbol_scopes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ type SymbolScope string | |||||||
| // List of symbol scopes | // List of symbol scopes | ||||||
| const ( | const ( | ||||||
| 	ScopeGlobal  SymbolScope = "GLOBAL" | 	ScopeGlobal  SymbolScope = "GLOBAL" | ||||||
| 	ScopeLocal               = "LOCAL" | 	ScopeLocal   SymbolScope = "LOCAL" | ||||||
| 	ScopeBuiltin             = "BUILTIN" | 	ScopeBuiltin SymbolScope = "BUILTIN" | ||||||
| 	ScopeFree                = "FREE" | 	ScopeFree    SymbolScope = "FREE" | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								vendor/github.com/d5/tengo/compiler/symbol_table.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/d5/tengo/compiler/symbol_table.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -64,9 +64,7 @@ func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool) | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !t.block { | 		depth++ | ||||||
| 			depth++ |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// if symbol is defined in parent table and if it's not global/builtin | 		// if symbol is defined in parent table and if it's not global/builtin | ||||||
| 		// then it's free variable. | 		// then it's free variable. | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								vendor/github.com/d5/tengo/objects/break.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/d5/tengo/objects/break.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,37 +0,0 @@ | |||||||
| package objects |  | ||||||
|  |  | ||||||
| import "github.com/d5/tengo/compiler/token" |  | ||||||
|  |  | ||||||
| // Break represents a break statement. |  | ||||||
| type Break struct{} |  | ||||||
|  |  | ||||||
| // TypeName returns the name of the type. |  | ||||||
| func (o *Break) TypeName() string { |  | ||||||
| 	return "break" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (o *Break) String() string { |  | ||||||
| 	return "<break>" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // BinaryOp returns another object that is the result of |  | ||||||
| // a given binary operator and a right-hand side object. |  | ||||||
| func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) { |  | ||||||
| 	return nil, ErrInvalidOperator |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Copy returns a copy of the type. |  | ||||||
| func (o *Break) Copy() Object { |  | ||||||
| 	return &Break{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsFalsy returns true if the value of the type is falsy. |  | ||||||
| func (o *Break) IsFalsy() bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Equals returns true if the value of the type |  | ||||||
| // is equal to the value of another object. |  | ||||||
| func (o *Break) Equals(x Object) bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								vendor/github.com/d5/tengo/objects/builtin_format.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/d5/tengo/objects/builtin_format.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | package objects | ||||||
|  |  | ||||||
|  | func builtinFormat(args ...Object) (Object, error) { | ||||||
|  | 	numArgs := len(args) | ||||||
|  | 	if numArgs == 0 { | ||||||
|  | 		return nil, ErrWrongNumArguments | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	format, ok := args[0].(*String) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, ErrInvalidArgumentType{ | ||||||
|  | 			Name:     "format", | ||||||
|  | 			Expected: "string", | ||||||
|  | 			Found:    args[0].TypeName(), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if numArgs == 1 { | ||||||
|  | 		return format, nil // okay to return 'format' directly as String is immutable | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s, err := Format(format.Value, args[1:]...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &String{Value: s}, nil | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								vendor/github.com/d5/tengo/objects/builtin_json.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/d5/tengo/objects/builtin_json.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,60 +0,0 @@ | |||||||
| package objects |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
|  |  | ||||||
| 	"github.com/d5/tengo" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // to_json(v object) => bytes |  | ||||||
| func builtinToJSON(args ...Object) (Object, error) { |  | ||||||
| 	if len(args) != 1 { |  | ||||||
| 		return nil, ErrWrongNumArguments |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	res, err := json.Marshal(objectToInterface(args[0])) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return &Error{Value: &String{Value: err.Error()}}, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(res) > tengo.MaxBytesLen { |  | ||||||
| 		return nil, ErrBytesLimit |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &Bytes{Value: res}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // from_json(data string/bytes) => object |  | ||||||
| func builtinFromJSON(args ...Object) (Object, error) { |  | ||||||
| 	if len(args) != 1 { |  | ||||||
| 		return nil, ErrWrongNumArguments |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var target interface{} |  | ||||||
|  |  | ||||||
| 	switch o := args[0].(type) { |  | ||||||
| 	case *Bytes: |  | ||||||
| 		err := json.Unmarshal(o.Value, &target) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return &Error{Value: &String{Value: err.Error()}}, nil |  | ||||||
| 		} |  | ||||||
| 	case *String: |  | ||||||
| 		err := json.Unmarshal([]byte(o.Value), &target) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return &Error{Value: &String{Value: err.Error()}}, nil |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		return nil, ErrInvalidArgumentType{ |  | ||||||
| 			Name:     "first", |  | ||||||
| 			Expected: "bytes/string", |  | ||||||
| 			Found:    args[0].TypeName(), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	res, err := FromInterface(target) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return res, nil |  | ||||||
| } |  | ||||||
							
								
								
									
										23
									
								
								vendor/github.com/d5/tengo/objects/builtin_module.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/d5/tengo/objects/builtin_module.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package objects | ||||||
|  |  | ||||||
|  | // BuiltinModule is an importable module that's written in Go. | ||||||
|  | type BuiltinModule struct { | ||||||
|  | 	Attrs map[string]Object | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Import returns an immutable map for the module. | ||||||
|  | func (m *BuiltinModule) Import(moduleName string) (interface{}, error) { | ||||||
|  | 	return m.AsImmutableMap(moduleName), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AsImmutableMap converts builtin module into an immutable map. | ||||||
|  | func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap { | ||||||
|  | 	attrs := make(map[string]Object, len(m.Attrs)) | ||||||
|  | 	for k, v := range m.Attrs { | ||||||
|  | 		attrs[k] = v.Copy() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	attrs["__module_name__"] = &String{Value: moduleName} | ||||||
|  |  | ||||||
|  | 	return &ImmutableMap{Value: attrs} | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								vendor/github.com/d5/tengo/objects/builtin_print.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/d5/tengo/objects/builtin_print.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,83 +0,0 @@ | |||||||
| package objects |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	"github.com/d5/tengo" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // print(args...) |  | ||||||
| func builtinPrint(args ...Object) (Object, error) { |  | ||||||
| 	for _, arg := range args { |  | ||||||
| 		if str, ok := arg.(*String); ok { |  | ||||||
| 			fmt.Println(str.Value) |  | ||||||
| 		} else { |  | ||||||
| 			fmt.Println(arg.String()) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // printf("format", args...) |  | ||||||
| func builtinPrintf(args ...Object) (Object, error) { |  | ||||||
| 	numArgs := len(args) |  | ||||||
| 	if numArgs == 0 { |  | ||||||
| 		return nil, ErrWrongNumArguments |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	format, ok := args[0].(*String) |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, ErrInvalidArgumentType{ |  | ||||||
| 			Name:     "format", |  | ||||||
| 			Expected: "string", |  | ||||||
| 			Found:    args[0].TypeName(), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if numArgs == 1 { |  | ||||||
| 		fmt.Print(format) |  | ||||||
| 		return nil, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	formatArgs := make([]interface{}, numArgs-1, numArgs-1) |  | ||||||
| 	for idx, arg := range args[1:] { |  | ||||||
| 		formatArgs[idx] = objectToInterface(arg) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	fmt.Printf(format.Value, formatArgs...) |  | ||||||
|  |  | ||||||
| 	return nil, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // sprintf("format", args...) |  | ||||||
| func builtinSprintf(args ...Object) (Object, error) { |  | ||||||
| 	numArgs := len(args) |  | ||||||
| 	if numArgs == 0 { |  | ||||||
| 		return nil, ErrWrongNumArguments |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	format, ok := args[0].(*String) |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, ErrInvalidArgumentType{ |  | ||||||
| 			Name:     "format", |  | ||||||
| 			Expected: "string", |  | ||||||
| 			Found:    args[0].TypeName(), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if numArgs == 1 { |  | ||||||
| 		return format, nil // okay to return 'format' directly as String is immutable |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	formatArgs := make([]interface{}, numArgs-1, numArgs-1) |  | ||||||
| 	for idx, arg := range args[1:] { |  | ||||||
| 		formatArgs[idx] = objectToInterface(arg) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	s := fmt.Sprintf(format.Value, formatArgs...) |  | ||||||
|  |  | ||||||
| 	if len(s) > tengo.MaxStringLen { |  | ||||||
| 		return nil, ErrStringLimit |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &String{Value: s}, nil |  | ||||||
| } |  | ||||||
							
								
								
									
										12
									
								
								vendor/github.com/d5/tengo/objects/builtin_type_checks.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/d5/tengo/objects/builtin_type_checks.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -181,3 +181,15 @@ func builtinIsCallable(args ...Object) (Object, error) { | |||||||
|  |  | ||||||
| 	return FalseValue, nil | 	return FalseValue, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func builtinIsIterable(args ...Object) (Object, error) { | ||||||
|  | 	if len(args) != 1 { | ||||||
|  | 		return nil, ErrWrongNumArguments | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, ok := args[0].(Iterable); ok { | ||||||
|  | 		return TrueValue, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return FalseValue, nil | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								vendor/github.com/d5/tengo/objects/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/d5/tengo/objects/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,19 +2,7 @@ package objects | |||||||
|  |  | ||||||
| // Builtins contains all default builtin functions. | // Builtins contains all default builtin functions. | ||||||
| // Use GetBuiltinFunctions instead of accessing Builtins directly. | // Use GetBuiltinFunctions instead of accessing Builtins directly. | ||||||
| var Builtins = []BuiltinFunction{ | var Builtins = []*BuiltinFunction{ | ||||||
| 	{ |  | ||||||
| 		Name:  "print", |  | ||||||
| 		Value: builtinPrint, |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		Name:  "printf", |  | ||||||
| 		Value: builtinPrintf, |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		Name:  "sprintf", |  | ||||||
| 		Value: builtinSprintf, |  | ||||||
| 	}, |  | ||||||
| 	{ | 	{ | ||||||
| 		Name:  "len", | 		Name:  "len", | ||||||
| 		Value: builtinLen, | 		Value: builtinLen, | ||||||
| @@ -95,6 +83,10 @@ var Builtins = []BuiltinFunction{ | |||||||
| 		Name:  "is_immutable_map", | 		Name:  "is_immutable_map", | ||||||
| 		Value: builtinIsImmutableMap, | 		Value: builtinIsImmutableMap, | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		Name:  "is_iterable", | ||||||
|  | 		Value: builtinIsIterable, | ||||||
|  | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		Name:  "is_time", | 		Name:  "is_time", | ||||||
| 		Value: builtinIsTime, | 		Value: builtinIsTime, | ||||||
| @@ -115,50 +107,12 @@ var Builtins = []BuiltinFunction{ | |||||||
| 		Name:  "is_callable", | 		Name:  "is_callable", | ||||||
| 		Value: builtinIsCallable, | 		Value: builtinIsCallable, | ||||||
| 	}, | 	}, | ||||||
| 	{ |  | ||||||
| 		Name:  "to_json", |  | ||||||
| 		Value: builtinToJSON, |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		Name:  "from_json", |  | ||||||
| 		Value: builtinFromJSON, |  | ||||||
| 	}, |  | ||||||
| 	{ | 	{ | ||||||
| 		Name:  "type_name", | 		Name:  "type_name", | ||||||
| 		Value: builtinTypeName, | 		Value: builtinTypeName, | ||||||
| 	}, | 	}, | ||||||
| } | 	{ | ||||||
|  | 		Name:  "format", | ||||||
| // AllBuiltinFunctionNames returns a list of all default builtin function names. | 		Value: builtinFormat, | ||||||
| func AllBuiltinFunctionNames() []string { | 	}, | ||||||
| 	var names []string |  | ||||||
| 	for _, bf := range Builtins { |  | ||||||
| 		names = append(names, bf.Name) |  | ||||||
| 	} |  | ||||||
| 	return names |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetBuiltinFunctions returns a slice of builtin function objects. |  | ||||||
| // GetBuiltinFunctions removes the duplicate names, and, the returned builtin functions |  | ||||||
| // are not guaranteed to be in the same order as names. |  | ||||||
| func GetBuiltinFunctions(names ...string) []*BuiltinFunction { |  | ||||||
| 	include := make(map[string]bool) |  | ||||||
| 	for _, name := range names { |  | ||||||
| 		include[name] = true |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var builtinFuncs []*BuiltinFunction |  | ||||||
| 	for _, bf := range Builtins { |  | ||||||
| 		if include[bf.Name] { |  | ||||||
| 			bf := bf |  | ||||||
| 			builtinFuncs = append(builtinFuncs, &bf) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return builtinFuncs |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetAllBuiltinFunctions returns all builtin functions. |  | ||||||
| func GetAllBuiltinFunctions() []*BuiltinFunction { |  | ||||||
| 	return GetBuiltinFunctions(AllBuiltinFunctionNames()...) |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								vendor/github.com/d5/tengo/objects/bytes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/d5/tengo/objects/bytes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -57,7 +57,7 @@ func (o *Bytes) Equals(x Object) bool { | |||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return bytes.Compare(o.Value, t.Value) == 0 | 	return bytes.Equal(o.Value, t.Value) | ||||||
| } | } | ||||||
|  |  | ||||||
| // IndexGet returns an element (as Int) at a given index. | // IndexGet returns an element (as Int) at a given index. | ||||||
| @@ -79,3 +79,11 @@ func (o *Bytes) IndexGet(index Object) (res Object, err error) { | |||||||
|  |  | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Iterate creates a bytes iterator. | ||||||
|  | func (o *Bytes) Iterate() Iterator { | ||||||
|  | 	return &BytesIterator{ | ||||||
|  | 		v: o.Value, | ||||||
|  | 		l: len(o.Value), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								vendor/github.com/d5/tengo/objects/bytes_iterator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/d5/tengo/objects/bytes_iterator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | package objects | ||||||
|  |  | ||||||
|  | import "github.com/d5/tengo/compiler/token" | ||||||
|  |  | ||||||
|  | // BytesIterator represents an iterator for a string. | ||||||
|  | type BytesIterator struct { | ||||||
|  | 	v []byte | ||||||
|  | 	i int | ||||||
|  | 	l int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TypeName returns the name of the type. | ||||||
|  | func (i *BytesIterator) TypeName() string { | ||||||
|  | 	return "bytes-iterator" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *BytesIterator) String() string { | ||||||
|  | 	return "<bytes-iterator>" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BinaryOp returns another object that is the result of | ||||||
|  | // a given binary operator and a right-hand side object. | ||||||
|  | func (i *BytesIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { | ||||||
|  | 	return nil, ErrInvalidOperator | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsFalsy returns true if the value of the type is falsy. | ||||||
|  | func (i *BytesIterator) IsFalsy() bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Equals returns true if the value of the type | ||||||
|  | // is equal to the value of another object. | ||||||
|  | func (i *BytesIterator) Equals(Object) bool { | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Copy returns a copy of the type. | ||||||
|  | func (i *BytesIterator) Copy() Object { | ||||||
|  | 	return &BytesIterator{v: i.v, i: i.i, l: i.l} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Next returns true if there are more elements to iterate. | ||||||
|  | func (i *BytesIterator) Next() bool { | ||||||
|  | 	i.i++ | ||||||
|  | 	return i.i <= i.l | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Key returns the key or index value of the current element. | ||||||
|  | func (i *BytesIterator) Key() Object { | ||||||
|  | 	return &Int{Value: int64(i.i - 1)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Value returns the value of the current element. | ||||||
|  | func (i *BytesIterator) Value() Object { | ||||||
|  | 	return &Int{Value: int64(i.v[i.i-1])} | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								vendor/github.com/d5/tengo/objects/closure.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/d5/tengo/objects/closure.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,7 +7,7 @@ import ( | |||||||
| // Closure represents a function closure. | // Closure represents a function closure. | ||||||
| type Closure struct { | type Closure struct { | ||||||
| 	Fn   *CompiledFunction | 	Fn   *CompiledFunction | ||||||
| 	Free []*Object | 	Free []*ObjectPtr | ||||||
| } | } | ||||||
|  |  | ||||||
| // TypeName returns the name of the type. | // TypeName returns the name of the type. | ||||||
| @@ -29,7 +29,7 @@ func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) { | |||||||
| func (o *Closure) Copy() Object { | func (o *Closure) Copy() Object { | ||||||
| 	return &Closure{ | 	return &Closure{ | ||||||
| 		Fn:   o.Fn.Copy().(*CompiledFunction), | 		Fn:   o.Fn.Copy().(*CompiledFunction), | ||||||
| 		Free: append([]*Object{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers | 		Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								vendor/github.com/d5/tengo/objects/compiled_function.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/d5/tengo/objects/compiled_function.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,6 +10,7 @@ type CompiledFunction struct { | |||||||
| 	Instructions  []byte | 	Instructions  []byte | ||||||
| 	NumLocals     int // number of local variables (including function parameters) | 	NumLocals     int // number of local variables (including function parameters) | ||||||
| 	NumParameters int | 	NumParameters int | ||||||
|  | 	VarArgs       bool | ||||||
| 	SourceMap     map[int]source.Pos | 	SourceMap     map[int]source.Pos | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -34,6 +35,7 @@ func (o *CompiledFunction) Copy() Object { | |||||||
| 		Instructions:  append([]byte{}, o.Instructions...), | 		Instructions:  append([]byte{}, o.Instructions...), | ||||||
| 		NumLocals:     o.NumLocals, | 		NumLocals:     o.NumLocals, | ||||||
| 		NumParameters: o.NumParameters, | 		NumParameters: o.NumParameters, | ||||||
|  | 		VarArgs:       o.VarArgs, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -47,3 +49,14 @@ func (o *CompiledFunction) IsFalsy() bool { | |||||||
| func (o *CompiledFunction) Equals(x Object) bool { | func (o *CompiledFunction) Equals(x Object) bool { | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SourcePos returns the source position of the instruction at ip. | ||||||
|  | func (o *CompiledFunction) SourcePos(ip int) source.Pos { | ||||||
|  | 	for ip >= 0 { | ||||||
|  | 		if p, ok := o.SourceMap[ip]; ok { | ||||||
|  | 			return p | ||||||
|  | 		} | ||||||
|  | 		ip-- | ||||||
|  | 	} | ||||||
|  | 	return source.NoPos | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								vendor/github.com/d5/tengo/objects/continue.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/d5/tengo/objects/continue.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,38 +0,0 @@ | |||||||
| package objects |  | ||||||
|  |  | ||||||
| import "github.com/d5/tengo/compiler/token" |  | ||||||
|  |  | ||||||
| // Continue represents a continue statement. |  | ||||||
| type Continue struct { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // TypeName returns the name of the type. |  | ||||||
| func (o *Continue) TypeName() string { |  | ||||||
| 	return "continue" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (o *Continue) String() string { |  | ||||||
| 	return "<continue>" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // BinaryOp returns another object that is the result of |  | ||||||
| // a given binary operator and a right-hand side object. |  | ||||||
| func (o *Continue) BinaryOp(op token.Token, rhs Object) (Object, error) { |  | ||||||
| 	return nil, ErrInvalidOperator |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Copy returns a copy of the type. |  | ||||||
| func (o *Continue) Copy() Object { |  | ||||||
| 	return &Continue{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsFalsy returns true if the value of the type is falsy. |  | ||||||
| func (o *Continue) IsFalsy() bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Equals returns true if the value of the type |  | ||||||
| // is equal to the value of another object. |  | ||||||
| func (o *Continue) Equals(x Object) bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								vendor/github.com/d5/tengo/objects/conversion.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/d5/tengo/objects/conversion.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| package objects | package objects | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -158,8 +159,8 @@ func ToTime(o Object) (v time.Time, ok bool) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| // objectToInterface attempts to convert an object o to an interface{} value | // ToInterface attempts to convert an object o to an interface{} value | ||||||
| func objectToInterface(o Object) (res interface{}) { | func ToInterface(o Object) (res interface{}) { | ||||||
| 	switch o := o.(type) { | 	switch o := o.(type) { | ||||||
| 	case *Int: | 	case *Int: | ||||||
| 		res = o.Value | 		res = o.Value | ||||||
| @@ -176,13 +177,29 @@ func objectToInterface(o Object) (res interface{}) { | |||||||
| 	case *Array: | 	case *Array: | ||||||
| 		res = make([]interface{}, len(o.Value)) | 		res = make([]interface{}, len(o.Value)) | ||||||
| 		for i, val := range o.Value { | 		for i, val := range o.Value { | ||||||
| 			res.([]interface{})[i] = objectToInterface(val) | 			res.([]interface{})[i] = ToInterface(val) | ||||||
|  | 		} | ||||||
|  | 	case *ImmutableArray: | ||||||
|  | 		res = make([]interface{}, len(o.Value)) | ||||||
|  | 		for i, val := range o.Value { | ||||||
|  | 			res.([]interface{})[i] = ToInterface(val) | ||||||
| 		} | 		} | ||||||
| 	case *Map: | 	case *Map: | ||||||
| 		res = make(map[string]interface{}) | 		res = make(map[string]interface{}) | ||||||
| 		for key, v := range o.Value { | 		for key, v := range o.Value { | ||||||
| 			res.(map[string]interface{})[key] = objectToInterface(v) | 			res.(map[string]interface{})[key] = ToInterface(v) | ||||||
| 		} | 		} | ||||||
|  | 	case *ImmutableMap: | ||||||
|  | 		res = make(map[string]interface{}) | ||||||
|  | 		for key, v := range o.Value { | ||||||
|  | 			res.(map[string]interface{})[key] = ToInterface(v) | ||||||
|  | 		} | ||||||
|  | 	case *Time: | ||||||
|  | 		res = o.Value | ||||||
|  | 	case *Error: | ||||||
|  | 		res = errors.New(o.String()) | ||||||
|  | 	case *Undefined: | ||||||
|  | 		res = nil | ||||||
| 	case Object: | 	case Object: | ||||||
| 		return o | 		return o | ||||||
| 	} | 	} | ||||||
| @@ -237,7 +254,7 @@ func FromInterface(v interface{}) (Object, error) { | |||||||
| 	case []Object: | 	case []Object: | ||||||
| 		return &Array{Value: v}, nil | 		return &Array{Value: v}, nil | ||||||
| 	case []interface{}: | 	case []interface{}: | ||||||
| 		arr := make([]Object, len(v), len(v)) | 		arr := make([]Object, len(v)) | ||||||
| 		for i, e := range v { | 		for i, e := range v { | ||||||
| 			vo, err := FromInterface(e) | 			vo, err := FromInterface(e) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								vendor/github.com/d5/tengo/objects/count_objects.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/d5/tengo/objects/count_objects.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | package objects | ||||||
|  |  | ||||||
|  | // CountObjects returns the number of objects that a given object o contains. | ||||||
|  | // For scalar value types, it will always be 1. For compound value types, | ||||||
|  | // this will include its elements and all of their elements recursively. | ||||||
|  | func CountObjects(o Object) (c int) { | ||||||
|  | 	c = 1 | ||||||
|  |  | ||||||
|  | 	switch o := o.(type) { | ||||||
|  | 	case *Array: | ||||||
|  | 		for _, v := range o.Value { | ||||||
|  | 			c += CountObjects(v) | ||||||
|  | 		} | ||||||
|  | 	case *ImmutableArray: | ||||||
|  | 		for _, v := range o.Value { | ||||||
|  | 			c += CountObjects(v) | ||||||
|  | 		} | ||||||
|  | 	case *Map: | ||||||
|  | 		for _, v := range o.Value { | ||||||
|  | 			c += CountObjects(v) | ||||||
|  | 		} | ||||||
|  | 	case *ImmutableMap: | ||||||
|  | 		for _, v := range o.Value { | ||||||
|  | 			c += CountObjects(v) | ||||||
|  | 		} | ||||||
|  | 	case *Error: | ||||||
|  | 		c += CountObjects(o.Value) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										1212
									
								
								vendor/github.com/d5/tengo/objects/formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1212
									
								
								vendor/github.com/d5/tengo/objects/formatter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user