forked from lug/matterbridge
		
	Compare commits
	
		
			220 Commits
		
	
	
		
			v1.14.0-rc
			...
			v1.17.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 148f7d2a91 | ||
|   | 1aa662f763 | ||
|   | 0b86b88de7 | ||
|   | 98033b1ba7 | ||
|   | 2b7eab629d | ||
|   | 0e4973e15c | ||
|   | af0acf0dae | ||
|   | 76e5fe5a87 | ||
|   | 802c80f40c | ||
|   | a51c5bd905 | ||
|   | 8c68556f52 | ||
|   | cca1ea2404 | ||
|   | 281016a501 | ||
|   | d4acdf2f89 | ||
|   | 0951e75c85 | ||
|   | 6b017b226a | ||
|   | 9e3bd7398c | ||
|   | 79f764c7a8 | ||
|   | b5dc4353fb | ||
|   | 2fbac73c29 | ||
|   | 6616d105d1 | ||
|   | 6b4b19194e | ||
|   | 9785edd263 | ||
|   | 2a0bc11b68 | ||
|   | dd0325a88d | ||
|   | 20783c0978 | ||
|   | 3f06a40bd5 | ||
|   | 68f43985ad | ||
|   | 915ca8f817 | ||
|   | a65a81610b | ||
|   | 8eb6ed5639 | ||
|   | 795a8705c3 | ||
|   | 3af0dc3b3a | ||
|   | 9cf9b958a3 | ||
|   | 3ac2ba8d5a | ||
|   | d893421c7b | ||
|   | 250b3bb579 | ||
|   | e9edbfc051 | ||
|   | e343db6f72 | ||
|   | 4d57d66f85 | ||
|   | 54ed6320c2 | ||
|   | 23083f3ae0 | ||
|   | 1985873494 | ||
|   | 8ae5917659 | ||
|   | c91bfd08d8 | ||
|   | 49110a5872 | ||
|   | c01c8edeb8 | ||
|   | ff8cf067b8 | ||
|   | 1420f68050 | ||
|   | c0be3e585a | ||
|   | 3049ef9151 | ||
|   | 4be00bbe6b | ||
|   | 9382dde098 | ||
|   | 1bf46b7711 | ||
|   | b85bae31d9 | ||
|   | 0898829313 | ||
|   | f8ad877601 | ||
|   | 585d1556c1 | ||
|   | 7486555875 | ||
|   | fc30b1bacc | ||
|   | 0dd19af6e8 | ||
|   | 4c44515f9d | ||
|   | 9d84d6dd64 | ||
|   | 0f708daf2d | ||
|   | b9354de8fd | ||
|   | c9d5f4c898 | ||
|   | 810c150781 | ||
|   | 31dd538c0b | ||
|   | 62e38e7c45 | ||
|   | b9da28a29b | ||
|   | 84bfa8a6b1 | ||
|   | 1f830963f6 | ||
|   | 12d2c6fe89 | ||
|   | f43faf15f8 | ||
|   | 173a38a374 | ||
|   | 1604ff15b5 | ||
|   | 214fe502cd | ||
|   | aae45a8179 | ||
|   | 075ca9ca47 | ||
|   | d4253d7a55 | ||
|   | 0917dc8766 | ||
|   | aba86855b5 | ||
|   | ed5386c213 | ||
|   | 455e75e92f | ||
|   | c394de0c88 | ||
|   | bad1990173 | ||
|   | 0bc159341d | ||
|   | 45bf1fd63a | ||
|   | ff0de85817 | ||
|   | 727fa9f929 | ||
|   | 0b9bc18236 | ||
|   | bad3b83d33 | ||
|   | 00967a98ac | ||
|   | 1d708ab351 | ||
|   | ba6759010b | ||
|   | da3868c104 | ||
|   | 0abf4d5d5d | ||
|   | 9b320cd43f | ||
|   | 28783a4146 | ||
|   | f92927eae5 | ||
|   | 294139ce7a | ||
|   | 45becd2573 | ||
|   | a3bee01e0a | ||
|   | 1dc93ec4f0 | ||
|   | 3562d4220c | ||
|   | 1532f6e427 | ||
|   | 9327810bbf | ||
|   | f66d5f1e58 | ||
|   | cec086994e | ||
|   | 942d8f1ced | ||
|   | 1552dcb143 | ||
|   | d525f1c9e4 | ||
|   | 921f2dfcdf | ||
|   | 79a006c8de | ||
|   | ff27746c0c | ||
|   | 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 | ||
|   | 858e16d34f | ||
|   | a60e62efb1 | ||
|   | 97f9d4be67 | ||
|   | fa4eec41f7 | ||
|   | 77516c97db | ||
|   | cba01f0865 | ||
|   | 8b754017ca | ||
|   | a27600046e | ||
|   | fb2667631d | ||
|   | b638f7037a | ||
|   | 74699a8262 | ||
|   | eabf2a4582 | ||
|   | 325d62b41c | ||
|   | e955a056e2 | ||
|   | 723f8c5fd5 | ||
|   | a16137f53f | ||
|   | d60b8b97f9 | ||
|   | 7b0bc51183 | ||
|   | 53aa076555 | ||
|   | f57370f33a | ||
|   | c557d51b6f | ||
|   | df3fdc26a0 | ||
|   | af00c34aac | ||
|   | 120bf39f55 | ||
|   | 26a7e35f27 | ||
|   | d44d2a5f00 | ||
|   | 7f1d86b338 | ||
|   | d8816280f0 | ||
|   | b09a73040f | ||
|   | 740b5f2602 | ||
|   | 96841c70c7 | ||
|   | f92735d35d | 
							
								
								
									
										3
									
								
								.fixmie.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.fixmie.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | go: | ||||||
|  |   comments: | ||||||
|  |     disabled: true | ||||||
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/Bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/Bug_report.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| --- | --- | ||||||
| name: Bug report | name: Bug report | ||||||
| about: Create a report to help us improve. (Check the FAQ on the wiki first) | about: Create a report to help us improve. (Check the FAQ on the wiki first) | ||||||
|  | labels: bug | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/Feature_request.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/Feature_request.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| --- | --- | ||||||
| name: Feature request | name: Feature request | ||||||
| about: Suggest an idea for this project | about: Suggest an idea for this project | ||||||
|  | labels: enhancement | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | # Exclude matterbridge binary | ||||||
|  | /matterbridge | ||||||
|  | /matterbridge.exe | ||||||
|  |  | ||||||
|  | # 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 | ||||||
| @@ -23,7 +23,7 @@ run: | |||||||
|   # default value is empty list, but next dirs are always skipped independently |   # default value is empty list, but next dirs are always skipped independently | ||||||
|   # from this option's value: |   # from this option's value: | ||||||
|   #   	vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ |   #   	vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ | ||||||
|   skip-dirs: |   skip-dirs: gateway/bridgemap$ | ||||||
|  |  | ||||||
|   # which files to skip: they will be analyzed, but issues from them |   # which files to skip: they will be analyzed, but issues from them | ||||||
|   # won't be reported. Default value is empty list, but there is |   # won't be reported. Default value is empty list, but there is | ||||||
| @@ -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 | ||||||
| @@ -158,7 +154,6 @@ linters-settings: | |||||||
|       - regexpMust |       - regexpMust | ||||||
|       - singleCaseSwitch |       - singleCaseSwitch | ||||||
|       - sloppyLen |       - sloppyLen | ||||||
|       - sloppyReassign |  | ||||||
|       - switchTrue |       - switchTrue | ||||||
|       - typeSwitchVar |       - typeSwitchVar | ||||||
|       - typeUnparen |       - typeUnparen | ||||||
| @@ -179,6 +174,8 @@ linters: | |||||||
|     - lll |     - lll | ||||||
|     - maligned |     - maligned | ||||||
|     - prealloc |     - prealloc | ||||||
|  |     - wsl | ||||||
|  |     - gomnd | ||||||
|  |  | ||||||
|  |  | ||||||
| # rules to deal with reported isues | # rules to deal with reported isues | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								.goreleaser.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.goreleaser.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | release: | ||||||
|  |   prerelease: auto | ||||||
|  |   name_template: "{{.ProjectName}} v{{.Version}}" | ||||||
|  |  | ||||||
|  | builds: | ||||||
|  | - env: | ||||||
|  |     - CGO_ENABLED=0 | ||||||
|  |   goos: | ||||||
|  |     - freebsd | ||||||
|  |     - windows | ||||||
|  |     - darwin | ||||||
|  |     - linux | ||||||
|  |     - dragonfly | ||||||
|  |     - netbsd | ||||||
|  |     - openbsd | ||||||
|  |   goarch: | ||||||
|  |     - amd64 | ||||||
|  |     - arm | ||||||
|  |     - arm64 | ||||||
|  |     - 386 | ||||||
|  |   ldflags: | ||||||
|  |     - -s -w -X main.githash={{.ShortCommit}} | ||||||
|  |  | ||||||
|  | archives: | ||||||
|  |   - | ||||||
|  |     id: matterbridge | ||||||
|  |     builds: | ||||||
|  |     - matterbridge | ||||||
|  |     name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" | ||||||
|  |     format: binary | ||||||
|  |     files: | ||||||
|  |       - none* | ||||||
|  |     replacements: | ||||||
|  |       386: 32bit | ||||||
|  |       amd64: 64bit | ||||||
|  |  | ||||||
|  | checksum: | ||||||
|  |   name_template: 'checksums.txt' | ||||||
							
								
								
									
										73
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,57 +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 |  | ||||||
|     - 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: | branches: | ||||||
|   # Get version info from tags. |   only: | ||||||
|   - MY_VERSION="$(git describe --tags)" |   - master | ||||||
|   # 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: | jobs: | ||||||
|   # Run the linter. |   include: | ||||||
|   - golangci-lint run |   - stage: lint | ||||||
|   # Run all the tests with the race detector and generate coverage. |     # Run linting in one Go environment only. | ||||||
|   - go test -v -race -coverprofile c.out ./... |     script: ./ci/lint.sh | ||||||
|   # Run the build script to generate the necessary binaries and images. |     go: 1.14.x | ||||||
|   - /bin/bash ci/bintray.sh |     env: | ||||||
|  |     - GO111MODULE=on | ||||||
|  |     - GOLANGCI_VERSION="v1.23.7" | ||||||
|  |   - stage: test | ||||||
|  |     # Run tests in a combination of Go environments. | ||||||
|  |     script: ./ci/test.sh | ||||||
|  |     go: 1.13.x | ||||||
|  |     env: | ||||||
|  |     - GOFLAGS=-mod=vendor | ||||||
|  |   - script: ./ci/test.sh | ||||||
|  |     go: 1.13.x | ||||||
|  |     env: | ||||||
|  |     - GO111MODULE=on | ||||||
|  |   - script: ./ci/test.sh | ||||||
|  |     go: 1.14.x | ||||||
|  |     env: | ||||||
|  |     - GO111MODULE=on | ||||||
|  |     - REPORT_COVERAGE=1 | ||||||
|  |     - BINDEPLOY=1 | ||||||
|  |  | ||||||
| after_script: | before_deploy: /bin/bash ci/bintray.sh | ||||||
|   # Upload test coverage to CodeClimate. |  | ||||||
|   - ./cc-test-reporter after-build --exit-code ${TRAVIS_TEST_RESULT} |  | ||||||
|  |  | ||||||
| deploy: | deploy: | ||||||
|  |   on: | ||||||
|  |     all_branches: true | ||||||
|  |     condition: $BINDEPLOY = 1 | ||||||
|   provider: bintray |   provider: bintray | ||||||
|   edge: |   edge: | ||||||
|     branch: v1.8.47 |     branch: v1.8.47 | ||||||
|   file: ci/deploy.json |   file: ci/deploy.json | ||||||
|   user: 42wim |   user: 42wim | ||||||
|   key: |   key: | ||||||
|      secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" |     secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,11 +1,13 @@ | |||||||
| FROM alpine:edge | FROM alpine:edge AS builder | ||||||
| ENTRYPOINT ["/bin/matterbridge"] |  | ||||||
|  |  | ||||||
| COPY . /go/src/github.com/42wim/matterbridge | COPY . /go/src/github.com/42wim/matterbridge | ||||||
| RUN apk update && apk add go git gcc musl-dev ca-certificates \ | RUN apk update && apk add go git gcc musl-dev \ | ||||||
|         && cd /go/src/github.com/42wim/matterbridge \ |         && cd /go/src/github.com/42wim/matterbridge \ | ||||||
|         && export GOPATH=/go \ |         && export GOPATH=/go \ | ||||||
|         && go get \ |         && go get \ | ||||||
|         && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \ |         && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge | ||||||
|         && rm -rf /go \ |  | ||||||
|         && apk del --purge git go gcc musl-dev | FROM alpine:edge | ||||||
|  | RUN apk --no-cache add ca-certificates mailcap | ||||||
|  | COPY --from=builder /bin/matterbridge /bin/matterbridge | ||||||
|  | ENTRYPOINT ["/bin/matterbridge"] | ||||||
|   | |||||||
							
								
								
									
										353
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										353
									
								
								README.md
									
									
									
									
									
								
							| @@ -3,156 +3,200 @@ | |||||||
| # matterbridge | # matterbridge | ||||||
|  |  | ||||||
| <br /> | <br /> | ||||||
|    **A simple chat bridge**<br /> | **A simple chat bridge**<br /> | ||||||
|    Letting people be where they want to be.<br /> | Letting people be where they want to be.<br /> | ||||||
|    <sub>Bridges between a growing number of protocols. Click below to demo or join the development chat.</sub> | <sub>Bridges between a growing number of protocols. Click below to demo or join the development chat.</sub> | ||||||
|  |  | ||||||
|    <sup> |    <sup> | ||||||
|  |  | ||||||
|    [Gitter][mb-gitter] | | [Gitter][mb-gitter] | | ||||||
|    [IRC][mb-irc] | | [IRC][mb-irc] | | ||||||
|       [Discord][mb-discord] | | [Discord][mb-discord] | | ||||||
|       [Matrix][mb-matrix] | | [Matrix][mb-matrix] | | ||||||
|       [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] | | ||||||
|       [Twitch][mb-twitch] | | [Twitch][mb-twitch] | | ||||||
|       [WhatsApp][mb-whatsapp] | | [WhatsApp][mb-whatsapp] | | ||||||
|       [Zulip][mb-zulip] | | [Zulip][mb-zulip] | | ||||||
|       [Telegram][mb-telegram] | | [Telegram][mb-telegram] | | ||||||
|       And more... | [Keybase][mb-keybase] | | ||||||
|    </sup> | [MSTeams][mb-msteams] | | ||||||
|  | And more... | ||||||
|  | </sup> | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
| ---- |  | ||||||
| [](https://github.com/42wim/matterbridge/releases/latest) | [](https://github.com/42wim/matterbridge/releases/latest) | ||||||
|    [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion) | [](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion) | ||||||
|    [](https://codeclimate.com/github/42wim/matterbridge/maintainability) | [](https://codeclimate.com/github/42wim/matterbridge/maintainability) | ||||||
|    [](https://codeclimate.com/github/42wim/matterbridge/test_coverage)<br /> | [](https://codeclimate.com/github/42wim/matterbridge/test_coverage)<br /> | ||||||
|  |  | ||||||
|   <hr /> |   <hr /> | ||||||
| </div> | </div> | ||||||
| <div align="right"><sup> | <div align="right"><sup> | ||||||
|  |  | ||||||
| **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> | ||||||
|  |  | ||||||
| ### Table of Contents | <p> | ||||||
|  * [Features](https://github.com/42wim/matterbridge/wiki/Features) |   <a href="https://www.digitalocean.com/"> | ||||||
|    * [Natively supported](#natively-supported) |     <img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_blue.svg" width="201px"> | ||||||
|    * [3rd party via matterbridge api](#3rd-party-via-matterbridge-api) |   </a> | ||||||
|    * [API](#API) | </p> | ||||||
|  * [Chat with us](#chat-with-us) |  | ||||||
|  * [Screenshots](https://github.com/42wim/matterbridge/wiki/) | # Table of Contents | ||||||
|  * [Installing](#installing) |  | ||||||
|    * [Binaries](#binaries) | - [matterbridge](#matterbridge) | ||||||
|    * [Building](#building) | - [Table of Contents](#table-of-contents) | ||||||
|  * [Configuration](#configuration) |   - [Features](#features) | ||||||
|    * [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) |     - [Natively supported](#natively-supported) | ||||||
|    * [Examples](#examples) |     - [3rd party via matterbridge api](#3rd-party-via-matterbridge-api) | ||||||
|  * [Running](#running) |     - [API](#api) | ||||||
|    * [Docker](#docker) |   - [Chat with us](#chat-with-us) | ||||||
|  * [Changelog](#changelog) |   - [Screenshots](#screenshots) | ||||||
|  * [FAQ](#faq) |   - [Installing / upgrading](#installing--upgrading) | ||||||
|  * [Related projects](#related-projects) |     - [Binaries](#binaries) | ||||||
|  * [Articles](#articles) |     - [Packages](#packages) | ||||||
|  * [Thanks](#thanks) |   - [Building](#building) | ||||||
|  |   - [Configuration](#configuration) | ||||||
|  |     - [Basic configuration](#basic-configuration) | ||||||
|  |     - [Settings](#settings) | ||||||
|  |     - [Advanced configuration](#advanced-configuration) | ||||||
|  |     - [Examples](#examples) | ||||||
|  |       - [Bridge mattermost (off-topic) - irc (#testing)](#bridge-mattermost-off-topic---irc-testing) | ||||||
|  |       - [Bridge slack (#general) - discord (general)](#bridge-slack-general---discord-general) | ||||||
|  |   - [Running](#running) | ||||||
|  |     - [Docker](#docker) | ||||||
|  |   - [Changelog](#changelog) | ||||||
|  |   - [FAQ](#faq) | ||||||
|  |   - [Related projects](#related-projects) | ||||||
|  |   - [Articles](#articles) | ||||||
|  |   - [Thanks](#thanks) | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| * [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols) |  | ||||||
| * [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols) | - [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols) | ||||||
| * [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes) | - [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols) | ||||||
| * Preserves threading when possible | - [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes) | ||||||
| * [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling) | - Preserves threading when possible | ||||||
| * [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing) | - [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling) | ||||||
| * [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups) | - [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing) | ||||||
| * [API](https://github.com/42wim/matterbridge/wiki/Features#api) | - [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups) | ||||||
|  | - [API](https://github.com/42wim/matterbridge/wiki/Features#api) | ||||||
|  |  | ||||||
| ### Natively supported | ### Natively supported | ||||||
|  |  | ||||||
| * [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x | - [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x | ||||||
| * [IRC](http://www.mirc.com/servers.html) | - [IRC](http://www.mirc.com/servers.html) | ||||||
| * [XMPP](https://xmpp.org) | - [XMPP](https://xmpp.org) | ||||||
| * [Gitter](https://gitter.im) | - [Gitter](https://gitter.im) | ||||||
| * [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) | - [Microsoft Teams](https://teams.microsoft.com) | ||||||
| * [Steam](https://store.steampowered.com/) | - [Steam](https://store.steampowered.com/) | ||||||
| * [Twitch](https://twitch.tv) | - [Twitch](https://twitch.tv) | ||||||
| * [Ssh-chat](https://github.com/shazow/ssh-chat) | - [Ssh-chat](https://github.com/shazow/ssh-chat) | ||||||
| * [WhatsApp](https://www.whatsapp.com/) | - [WhatsApp](https://www.whatsapp.com/) | ||||||
| * [Zulip](https://zulipchat.com) | - [Zulip](https://zulipchat.com) | ||||||
|  | - [Keybase](https://keybase.io) | ||||||
|  |  | ||||||
| ### 3rd party via matterbridge api | ### 3rd party via matterbridge api | ||||||
| * [Minecraft](https://github.com/elytra/MatterLink) |  | ||||||
| * [Reddit](https://github.com/bonehurtingjuice/mattereddit) | - [Minecraft](https://github.com/elytra/MatterLink) | ||||||
| * [Facebook messenger](https://github.com/VictorNine/fbridge) | - [Reddit](https://github.com/bonehurtingjuice/mattereddit) | ||||||
|  | - [Facebook messenger](https://github.com/VictorNine/fbridge) | ||||||
|  | - [Discourse](https://github.com/DeclanHoare/matterbabble) | ||||||
|  | - [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430) | ||||||
|  |  | ||||||
| ### API | ### 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. | ||||||
|  |  | ||||||
| * [MatterLink](https://github.com/elytra/MatterLink) (Matterbridge link for Minecraft Server chat) | - [MatterLink](https://github.com/elytra/MatterLink) (Matterbridge link for Minecraft Server chat) | ||||||
| * [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot) | - [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot) | ||||||
| * [Mattereddit](https://github.com/bonehurtingjuice/mattereddit) (Reddit chat support) | - [Mattereddit](https://github.com/bonehurtingjuice/mattereddit) (Reddit chat support) | ||||||
| * [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support) | - [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support) | ||||||
|  | - [matterbabble](https://github.com/DeclanHoare/matterbabble) (Discourse support) | ||||||
|  | - [MatterAMXX](https://forums.alliedmods.net/showthread.php?t=319430) (Counter-Strike, half-life and more via AMXX mod) | ||||||
|  |  | ||||||
| ## Chat with us | ## Chat with us | ||||||
|  |  | ||||||
| Questions or want to test on your favorite platform? Join below: | Questions or want to test on your favorite platform? Join below: | ||||||
|  |  | ||||||
| * [Gitter][mb-gitter] | - [Gitter][mb-gitter] | ||||||
| * [IRC][mb-irc] | - [IRC][mb-irc] | ||||||
| * [Discord][mb-discord] | - [Discord][mb-discord] | ||||||
| * [Matrix][mb-matrix] | - [Matrix][mb-matrix] | ||||||
| * [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] | ||||||
|  | - [Keybase][mb-keybase] | ||||||
|  |  | ||||||
| ## 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.13.1](https://github.com/42wim/matterbridge/releases/latest) |  | ||||||
| * Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/) | - Latest stable release [v1.17.1](https://github.com/42wim/matterbridge/releases/latest) | ||||||
|  | - Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/) | ||||||
|  |  | ||||||
|  | To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. | ||||||
|  |  | ||||||
| ### Packages | ### Packages | ||||||
| * [Overview](https://repology.org/metapackage/matterbridge/versions) |  | ||||||
|  |  | ||||||
| ### Building | - [Overview](https://repology.org/metapackage/matterbridge/versions) | ||||||
| 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). |  | ||||||
|  | ## 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.12+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed. | ||||||
|  |  | ||||||
| After Go is setup, download matterbridge to your $GOPATH directory. |  | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| cd $GOPATH |  | ||||||
| go get github.com/42wim/matterbridge | go get github.com/42wim/matterbridge | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| You should now have matterbridge binary in the bin directory: | You should now have matterbridge binary in the ~/go/bin directory: | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| $ ls bin/ | $ ls ~/go/bin/ | ||||||
| matterbridge | matterbridge | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
|  |  | ||||||
| ### 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. | ||||||
|  |  | ||||||
| ### Examples | ### Examples | ||||||
|  |  | ||||||
| #### Bridge mattermost (off-topic) - irc (#testing) | #### Bridge mattermost (off-topic) - irc (#testing) | ||||||
|  |  | ||||||
| ```toml | ```toml | ||||||
| [irc] | [irc] | ||||||
|     [irc.freenode] |     [irc.freenode] | ||||||
| @@ -181,6 +225,7 @@ enable=true | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Bridge slack (#general) - discord (general) | #### Bridge slack (#general) - discord (general) | ||||||
|  |  | ||||||
| ```toml | ```toml | ||||||
| [slack] | [slack] | ||||||
| [slack.test] | [slack.test] | ||||||
| @@ -225,76 +270,86 @@ Usage of ./matterbridge: | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Docker | ### Docker | ||||||
| Create your matterbridge.toml file locally eg in `/tmp/matterbridge.toml` |  | ||||||
| ``` | Please take a look at the [Docker Wiki page](https://github.com/42wim/matterbridge/wiki/Deploy:-Docker) for more information. | ||||||
| docker run -ti -v /tmp/matterbridge.toml:/matterbridge.toml 42wim/matterbridge |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Changelog | ## Changelog | ||||||
|  |  | ||||||
| See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.md) | See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.md) | ||||||
|  |  | ||||||
| ## FAQ | ## FAQ | ||||||
|  |  | ||||||
| See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | ||||||
|  |  | ||||||
| Want to tip ? |  | ||||||
| * eth: 0xb3f9b5387c66ad6be892bcb7bbc67862f3abc16f |  | ||||||
| * btc: 1N7cKHj5SfqBHBzDJ6kad4BzeqUBBS2zhs |  | ||||||
|  |  | ||||||
| ## Related projects | ## Related projects | ||||||
| * [FOSSRIT/infrastructure - roles/matterbridge](https://github.com/FOSSRIT/infrastructure/tree/master/roles/matterbridge) (Ansible role used to automate deployments of Matterbridge) |  | ||||||
| * [matterbridge autoconfig](https://github.com/patcon/matterbridge-autoconfig) | - [jwflory/ansible-role-matterbridge](https://galaxy.ansible.com/jwflory/matterbridge) (Ansible role to simplify deploying Matterbridge) | ||||||
| * [matterbridge config viewer](https://github.com/patcon/matterbridge-heroku-viewer) | - [matterbridge autoconfig](https://github.com/patcon/matterbridge-autoconfig) | ||||||
| * [matterbridge-heroku](https://github.com/cadecairos/matterbridge-heroku) | - [matterbridge config viewer](https://github.com/patcon/matterbridge-heroku-viewer) | ||||||
| * [mattereddit](https://github.com/bonehurtingjuice/mattereddit) | - [matterbridge-heroku](https://github.com/cadecairos/matterbridge-heroku) | ||||||
| * [matterlink](https://github.com/elytra/MatterLink) | - [mattereddit](https://github.com/bonehurtingjuice/mattereddit) | ||||||
| * [mattermost-plugin](https://github.com/matterbridge/mattermost-plugin) - Run matterbridge as a plugin in mattermost | - [matterlink](https://github.com/elytra/MatterLink) | ||||||
| * [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot) | - [mattermost-plugin](https://github.com/matterbridge/mattermost-plugin) - Run matterbridge as a plugin in mattermost | ||||||
| * [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support) | - [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot) | ||||||
|  | - [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support) | ||||||
|  | - [isla](https://github.com/alphachung/isla) (Bot for Discord-Telegram groups used alongside matterbridge) | ||||||
|  | - [matterbabble](https://github.com/DeclanHoare/matterbabble) (Connect Discourse threads to Matterbridge) | ||||||
|  |  | ||||||
| ## Articles | ## Articles | ||||||
| * [matterbridge on kubernetes](https://medium.freecodecamp.org/using-kubernetes-to-deploy-a-chat-gateway-or-when-technology-works-like-its-supposed-to-a169a8cd69a3) |  | ||||||
| * https://mattermost.com/blog/connect-irc-to-mattermost/ | - [matterbridge on kubernetes](https://medium.freecodecamp.org/using-kubernetes-to-deploy-a-chat-gateway-or-when-technology-works-like-its-supposed-to-a169a8cd69a3) | ||||||
| * https://blog.valvin.fr/2016/09/17/mattermost-et-un-channel-irc-cest-possible/ | - https://mattermost.com/blog/connect-irc-to-mattermost/ | ||||||
| * https://blog.brightscout.com/top-10-mattermost-integrations/ | - https://blog.valvin.fr/2016/09/17/mattermost-et-un-channel-irc-cest-possible/ | ||||||
| * http://bencey.co.nz/2018/09/17/bridge/ | - https://blog.brightscout.com/top-10-mattermost-integrations/ | ||||||
| * https://www.algoo.fr/blog/2018/01/19/recouvrez-votre-liberte-en-quittant-slack-pour-un-mattermost-auto-heberge/ | - http://bencey.co.nz/2018/09/17/bridge/ | ||||||
| * https://kopano.com/blog/matterbridge-bridging-mattermost-chat/ | - https://www.algoo.fr/blog/2018/01/19/recouvrez-votre-liberte-en-quittant-slack-pour-un-mattermost-auto-heberge/ | ||||||
| * https://www.stitcher.com/s/?eid=52382713 | - https://kopano.com/blog/matterbridge-bridging-mattermost-chat/ | ||||||
| * https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/ | - https://www.stitcher.com/s/?eid=52382713 | ||||||
|  | - 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 |  | ||||||
| * echo - https://github.com/labstack/echo | - discord - https://github.com/bwmarrin/discordgo | ||||||
| * gitter - https://github.com/sromku/go-gitter | - echo - https://github.com/labstack/echo | ||||||
| * gops - https://github.com/google/gops | - gitter - https://github.com/sromku/go-gitter | ||||||
| * gozulipbot - https://github.com/ifo/gozulipbot | - gops - https://github.com/google/gops | ||||||
| * irc - https://github.com/lrstanley/girc | - gozulipbot - https://github.com/ifo/gozulipbot | ||||||
| * mattermost - https://github.com/mattermost/mattermost-server | - irc - https://github.com/lrstanley/girc | ||||||
| * matrix - https://github.com/matrix-org/gomatrix | - mattermost - https://github.com/mattermost/mattermost-server | ||||||
| * sshchat - https://github.com/shazow/ssh-chat | - matrix - https://github.com/matrix-org/gomatrix | ||||||
| * slack - https://github.com/nlopes/slack | - sshchat - https://github.com/shazow/ssh-chat | ||||||
| * steam - https://github.com/Philipp15b/go-steam | - slack - https://github.com/nlopes/slack | ||||||
| * telegram - https://github.com/go-telegram-bot-api/telegram-bot-api | - steam - https://github.com/Philipp15b/go-steam | ||||||
| * xmpp - https://github.com/mattn/go-xmpp | - telegram - https://github.com/go-telegram-bot-api/telegram-bot-api | ||||||
| * whatsapp - https://github.com/Rhymen/go-whatsapp/ | - xmpp - https://github.com/mattn/go-xmpp | ||||||
| * zulip - https://github.com/ifo/gozulipbot | - whatsapp - https://github.com/Rhymen/go-whatsapp/ | ||||||
| * tengo - https://github.com/d5/tengo | - zulip - https://github.com/ifo/gozulipbot | ||||||
|  | - tengo - https://github.com/d5/tengo | ||||||
|  | - keybase - https://github.com/keybase/go-keybase-chat-bot | ||||||
|  | - msgraph.go - https://github.com/yaegashi/msgraph.go | ||||||
|  |  | ||||||
| <!-- Links --> | <!-- Links --> | ||||||
|  |  | ||||||
|    [mb-gitter]: https://gitter.im/42wim/matterbridge | [mb-gitter]: https://gitter.im/42wim/matterbridge | ||||||
|    [mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat | [mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat | ||||||
|    [mb-discord]: https://discord.gg/AkKPtrQ | [mb-discord]: https://discord.gg/AkKPtrQ | ||||||
|    [mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org | [mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org | ||||||
|    [mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA | [mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA | ||||||
|    [mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e | [mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e | ||||||
|    [mb-rocketchat]: https://open.rocket.chat/channel/matterbridge | [mb-rocketchat]: https://open.rocket.chat/channel/matterbridge | ||||||
|    [mb-xmpp]: https://inverse.chat/ | [mb-xmpp]: https://inverse.chat/ | ||||||
|    [mb-twitch]: https://www.twitch.tv/matterbridge | [mb-twitch]: https://www.twitch.tv/matterbridge | ||||||
|    [mb-whatsapp]: https://www.whatsapp.com/ | [mb-whatsapp]: https://www.whatsapp.com/ | ||||||
|    [mb-zulip]: https://matterbridge.zulipchat.com/register/ | [mb-keybase]: https://keybase.io/team/matterbridge | ||||||
|    [mb-telegram]: https://t.me/Matterbridge | [mb-zulip]: https://matterbridge.zulipchat.com/register/ | ||||||
|  | [mb-telegram]: https://t.me/Matterbridge | ||||||
|  | [mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7 | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package bridge | package bridge | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"log" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
| @@ -41,6 +42,10 @@ type Factory func(*Config) Bridger | |||||||
|  |  | ||||||
| func New(bridge *config.Bridge) *Bridge { | func New(bridge *config.Bridge) *Bridge { | ||||||
| 	accInfo := strings.Split(bridge.Account, ".") | 	accInfo := strings.Split(bridge.Account, ".") | ||||||
|  | 	if len(accInfo) != 2 { | ||||||
|  | 		log.Fatalf("config failure, account incorrect: %s", bridge.Account) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	protocol := accInfo[0] | 	protocol := accInfo[0] | ||||||
| 	name := accInfo[1] | 	name := accInfo[1] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package config | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -76,9 +77,11 @@ type Protocol struct { | |||||||
| 	BindAddress            string // mattermost, slack // DEPRECATED | 	BindAddress            string // mattermost, slack // DEPRECATED | ||||||
| 	Buffer                 int    // api | 	Buffer                 int    // api | ||||||
| 	Charset                string // irc | 	Charset                string // irc | ||||||
|  | 	ClientID               string // msteams | ||||||
| 	ColorNicks             bool   // only irc for now | 	ColorNicks             bool   // only irc for now | ||||||
| 	Debug                  bool   // general | 	Debug                  bool   // general | ||||||
| 	DebugLevel             int    // only for irc now | 	DebugLevel             int    // only for irc now | ||||||
|  | 	DisableWebPagePreview  bool   // telegram | ||||||
| 	EditSuffix             string // mattermost, slack, discord, telegram, gitter | 	EditSuffix             string // mattermost, slack, discord, telegram, gitter | ||||||
| 	EditDisable            bool   // mattermost, slack, discord, telegram, gitter | 	EditDisable            bool   // mattermost, slack, discord, telegram, gitter | ||||||
| 	IconURL                string // mattermost, slack | 	IconURL                string // mattermost, slack | ||||||
| @@ -93,6 +96,7 @@ type Protocol struct { | |||||||
| 	MediaDownloadSize      int    // all protocols | 	MediaDownloadSize      int    // all protocols | ||||||
| 	MediaServerDownload    string | 	MediaServerDownload    string | ||||||
| 	MediaServerUpload      string | 	MediaServerUpload      string | ||||||
|  | 	MediaConvertWebPToPNG  bool       // telegram | ||||||
| 	MessageDelay           int        // IRC, time in millisecond to wait between messages | 	MessageDelay           int        // IRC, time in millisecond to wait between messages | ||||||
| 	MessageFormat          string     // telegram | 	MessageFormat          string     // telegram | ||||||
| 	MessageLength          int        // IRC, max length of a message allowed | 	MessageLength          int        // IRC, max length of a message allowed | ||||||
| @@ -115,31 +119,38 @@ type Protocol struct { | |||||||
| 	Protocol               string     // all protocols | 	Protocol               string     // all protocols | ||||||
| 	QuoteDisable           bool       // telegram | 	QuoteDisable           bool       // telegram | ||||||
| 	QuoteFormat            string     // telegram | 	QuoteFormat            string     // telegram | ||||||
|  | 	QuoteLengthLimit       int        // telegram | ||||||
| 	RejoinDelay            int        // IRC | 	RejoinDelay            int        // IRC | ||||||
| 	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 | ||||||
|  | 	SessionFile            string     // msteams,whatsapp | ||||||
| 	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 | ||||||
| 	Team                   string     // mattermost | 	Team                   string     // mattermost, keybase | ||||||
|  | 	TeamID                 string     // msteams | ||||||
|  | 	TenantID               string     // msteams | ||||||
| 	Token                  string     // gitter, slack, discord, api | 	Token                  string     // gitter, slack, discord, api | ||||||
| 	Topic                  string     // zulip | 	Topic                  string     // zulip | ||||||
| 	URL                    string     // mattermost, slack // DEPRECATED | 	URL                    string     // mattermost, slack // DEPRECATED | ||||||
| 	UseAPI                 bool       // mattermost, slack | 	UseAPI                 bool       // mattermost, slack | ||||||
|  | 	UseLocalAvatar         []string   // discord | ||||||
| 	UseSASL                bool       // IRC | 	UseSASL                bool       // IRC | ||||||
| 	UseTLS                 bool       // IRC | 	UseTLS                 bool       // IRC | ||||||
| 	UseDiscriminator       bool       // discord | 	UseDiscriminator       bool       // discord | ||||||
| 	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 | ||||||
| } | } | ||||||
| @@ -165,6 +176,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 | ||||||
| @@ -188,12 +206,15 @@ type BridgeValues struct { | |||||||
| 	SSHChat            map[string]Protocol | 	SSHChat            map[string]Protocol | ||||||
| 	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 | ||||||
|  | 	Keybase            map[string]Protocol | ||||||
| 	General            Protocol | 	General            Protocol | ||||||
|  | 	Tengo              Tengo | ||||||
| 	Gateway            []Gateway | 	Gateway            []Gateway | ||||||
| 	SameChannelGateway []SameChannelGateway | 	SameChannelGateway []SameChannelGateway | ||||||
| } | } | ||||||
|  |  | ||||||
| type Config interface { | type Config interface { | ||||||
|  | 	Viper() *viper.Viper | ||||||
| 	BridgeValues() *BridgeValues | 	BridgeValues() *BridgeValues | ||||||
| 	GetBool(key string) (bool, bool) | 	GetBool(key string) (bool, bool) | ||||||
| 	GetInt(key string) (int, bool) | 	GetInt(key string) (int, bool) | ||||||
| @@ -220,7 +241,8 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config { | |||||||
| 		logger.Fatalf("Failed to read configuration file: %#v", err) | 		logger.Fatalf("Failed to read configuration file: %#v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	mycfg := newConfigFromString(logger, input) | 	cfgtype := detectConfigType(cfgfile) | ||||||
|  | 	mycfg := newConfigFromString(logger, input, cfgtype) | ||||||
| 	if mycfg.cv.General.MediaDownloadSize == 0 { | 	if mycfg.cv.General.MediaDownloadSize == 0 { | ||||||
| 		mycfg.cv.General.MediaDownloadSize = 1000000 | 		mycfg.cv.General.MediaDownloadSize = 1000000 | ||||||
| 	} | 	} | ||||||
| @@ -231,25 +253,37 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config { | |||||||
| 	return mycfg | 	return mycfg | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // detectConfigType detects JSON and YAML formats, defaults to TOML. | ||||||
|  | func detectConfigType(cfgfile string) string { | ||||||
|  | 	fileExt := filepath.Ext(cfgfile) | ||||||
|  | 	switch fileExt { | ||||||
|  | 	case ".json": | ||||||
|  | 		return "json" | ||||||
|  | 	case ".yaml", ".yml": | ||||||
|  | 		return "yaml" | ||||||
|  | 	} | ||||||
|  | 	return "toml" | ||||||
|  | } | ||||||
|  |  | ||||||
| // NewConfigFromString instantiates a new configuration based on the specified string. | // NewConfigFromString instantiates a new configuration based on the specified string. | ||||||
| func NewConfigFromString(rootLogger *logrus.Logger, input []byte) Config { | func NewConfigFromString(rootLogger *logrus.Logger, input []byte) Config { | ||||||
| 	logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"}) | 	logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"}) | ||||||
| 	return newConfigFromString(logger, input) | 	return newConfigFromString(logger, input, "toml") | ||||||
| } | } | ||||||
|  |  | ||||||
| func newConfigFromString(logger *logrus.Entry, input []byte) *config { | func newConfigFromString(logger *logrus.Entry, input []byte, cfgtype string) *config { | ||||||
| 	viper.SetConfigType("toml") | 	viper.SetConfigType(cfgtype) | ||||||
| 	viper.SetEnvPrefix("matterbridge") | 	viper.SetEnvPrefix("matterbridge") | ||||||
| 	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) | 	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) | ||||||
| 	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, | ||||||
| @@ -262,6 +296,10 @@ func (c *config) BridgeValues() *BridgeValues { | |||||||
| 	return c.cv | 	return c.cv | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *config) Viper() *viper.Viper { | ||||||
|  | 	return c.v | ||||||
|  | } | ||||||
|  |  | ||||||
| func (c *config) GetBool(key string) (bool, bool) { | func (c *config) GetBool(key string) (bool, bool) { | ||||||
| 	c.RLock() | 	c.RLock() | ||||||
| 	defer c.RUnlock() | 	defer c.RUnlock() | ||||||
|   | |||||||
| @@ -10,7 +10,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/bridge/helper" | 	"github.com/42wim/matterbridge/bridge/helper" | ||||||
| 	"github.com/bwmarrin/discordgo" | 	"github.com/matterbridge/discordgo" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const MessageLength = 1950 | const MessageLength = 1950 | ||||||
| @@ -21,7 +21,7 @@ type Bdiscord struct { | |||||||
| 	c *discordgo.Session | 	c *discordgo.Session | ||||||
|  |  | ||||||
| 	nick            string | 	nick            string | ||||||
| 	useChannelID    bool | 	userID          string | ||||||
| 	guildID         string | 	guildID         string | ||||||
| 	webhookID       string | 	webhookID       string | ||||||
| 	webhookToken    string | 	webhookToken    string | ||||||
| @@ -72,9 +72,11 @@ func (b *Bdiscord) Connect() error { | |||||||
| 	} | 	} | ||||||
| 	b.Log.Info("Connection succeeded") | 	b.Log.Info("Connection succeeded") | ||||||
| 	b.c.AddHandler(b.messageCreate) | 	b.c.AddHandler(b.messageCreate) | ||||||
|  | 	b.c.AddHandler(b.messageTyping) | ||||||
| 	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() | ||||||
| @@ -91,15 +93,16 @@ func (b *Bdiscord) Connect() error { | |||||||
| 	} | 	} | ||||||
| 	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1) | 	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1) | ||||||
| 	b.nick = userinfo.Username | 	b.nick = userinfo.Username | ||||||
|  | 	b.userID = userinfo.ID | ||||||
| 	b.channelsMutex.Lock() | 	b.channelsMutex.Lock() | ||||||
| 	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() | ||||||
| @@ -113,30 +116,37 @@ func (b *Bdiscord) Connect() error { | |||||||
| 			b.Log.Infof("Server=\"%s\" # Server ID", guild.ID) | 			b.Log.Infof("Server=\"%s\" # Server ID", guild.ID) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.channelsMutex.RLock() | 	b.channelsMutex.RLock() | ||||||
| 	if b.GetString("WebhookURL") == "" { | 	if b.GetString("WebhookURL") == "" { | ||||||
| 		for _, channel := range b.channels { | 		for _, channel := range b.channels { | ||||||
| 			b.Log.Debugf("found channel %#v", channel) | 			b.Log.Debugf("found channel %#v", channel) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		b.canEditWebhooks = true | 		manageWebhooks := discordgo.PermissionManageWebhooks | ||||||
| 		for _, channel := range b.channels { | 		var channelsDenied []string | ||||||
| 			b.Log.Debugf("found channel %#v; verifying PermissionManageWebhooks", channel) | 		for _, info := range b.Channels { | ||||||
| 			perms, permsErr := b.c.State.UserChannelPermissions(userinfo.ID, channel.ID) | 			id := b.getChannelID(info.Name) // note(qaisjp): this readlocks channelsMutex | ||||||
| 			manageWebhooks := discordgo.PermissionManageWebhooks | 			b.Log.Debugf("Verifying PermissionManageWebhooks for %s with ID %s", info.ID, id) | ||||||
| 			if permsErr != nil || perms&manageWebhooks != manageWebhooks { |  | ||||||
| 				b.Log.Warnf("Can't manage webhooks in channel \"%s\"", channel.Name) | 			perms, permsErr := b.c.UserChannelPermissions(userinfo.ID, id) | ||||||
| 				b.canEditWebhooks = false | 			if permsErr != nil { | ||||||
|  | 				b.Log.Warnf("Failed to check PermissionManageWebhooks in channel \"%s\": %s", info.Name, permsErr.Error()) | ||||||
|  | 			} else if perms&manageWebhooks == manageWebhooks { | ||||||
|  | 				continue | ||||||
| 			} | 			} | ||||||
|  | 			channelsDenied = append(channelsDenied, fmt.Sprintf("%#v", info.Name)) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		b.canEditWebhooks = len(channelsDenied) == 0 | ||||||
| 		if b.canEditWebhooks { | 		if b.canEditWebhooks { | ||||||
| 			b.Log.Info("Can manage webhooks; will edit channel for global webhook on send") | 			b.Log.Info("Can manage webhooks; will edit channel for global webhook on send") | ||||||
| 		} else { | 		} else { | ||||||
| 			b.Log.Warn("Can't manage webhooks; won't edit channel for global webhook on send") | 			b.Log.Warn("Can't manage webhooks; won't edit channel for global webhook on send") | ||||||
|  | 			b.Log.Warn("Can't manage webhooks in channels: ", strings.Join(channelsDenied, ", ")) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	b.channelsMutex.RUnlock() | 	b.channelsMutex.RUnlock() | ||||||
| @@ -172,10 +182,6 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error { | |||||||
| 	defer b.channelsMutex.Unlock() | 	defer b.channelsMutex.Unlock() | ||||||
|  |  | ||||||
| 	b.channelInfoMap[channel.ID] = &channel | 	b.channelInfoMap[channel.ID] = &channel | ||||||
| 	idcheck := strings.Split(channel.Name, "ID:") |  | ||||||
| 	if len(idcheck) > 1 { |  | ||||||
| 		b.useChannelID = true |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -187,6 +193,14 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 		return "", fmt.Errorf("Could not find channelID for %v", msg.Channel) | 		return "", fmt.Errorf("Could not find channelID for %v", msg.Channel) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if msg.Event == config.EventUserTyping { | ||||||
|  | 		if b.GetBool("ShowUserTyping") { | ||||||
|  | 			err := b.c.ChannelTyping(channelID) | ||||||
|  | 			return "", err | ||||||
|  | 		} | ||||||
|  | 		return "", nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Make a action /me of the message | 	// Make a action /me of the message | ||||||
| 	if msg.Event == config.EventUserAction { | 	if msg.Event == config.EventUserAction { | ||||||
| 		msg.Text = "_" + msg.Text + "_" | 		msg.Text = "_" + msg.Text + "_" | ||||||
| @@ -208,26 +222,26 @@ 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 | ||||||
| 		} | 		} | ||||||
| 		b.Log.Debugf("Broadcasting using Webhook") |  | ||||||
| 		for _, f := range msg.Extra["file"] { | 		// If we are editing a message, delete the old message | ||||||
| 			fi := f.(config.FileInfo) | 		if msg.ID != "" { | ||||||
| 			if fi.Comment != "" { | 			b.Log.Debugf("Deleting edited webhook message") | ||||||
| 				msg.Text += fi.Comment + ": " | 			err := b.c.ChannelMessageDelete(channelID, msg.ID) | ||||||
| 			} | 			if err != nil { | ||||||
| 			if fi.URL != "" { | 				b.Log.Errorf("Could not delete edited webhook message: %s", err) | ||||||
| 				msg.Text = fi.URL |  | ||||||
| 				if fi.Comment != "" { |  | ||||||
| 					msg.Text = fi.Comment + ": " + fi.URL |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		b.Log.Debugf("Broadcasting using Webhook") | ||||||
|  |  | ||||||
| 		// skip empty messages | 		// skip empty messages | ||||||
| 		if msg.Text == "" { | 		if msg.Text == "" && (msg.Extra == nil || len(msg.Extra["file"]) == 0) { | ||||||
|  | 			b.Log.Debugf("Skipping empty message %#v", msg) | ||||||
| 			return "", nil | 			return "", nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -246,20 +260,20 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 			b.Log.Debugf("Setting webhook channel to \"%s\"", msg.Channel) | 			b.Log.Debugf("Setting webhook channel to \"%s\"", msg.Channel) | ||||||
| 			_, err := b.c.WebhookEdit(wID, "", "", channelID) | 			_, err := b.c.WebhookEdit(wID, "", "", channelID) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				b.Log.Errorf("Could not set webhook channel: %v", err) | 				b.Log.Errorf("Could not set webhook channel: %s", err) | ||||||
| 				return "", err | 				return "", err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		err := b.c.WebhookExecute( | 		b.Log.Debugf("Processing webhook sending for message %#v", msg) | ||||||
| 			wID, | 		msg, err := b.webhookSend(&msg, wID, wToken) | ||||||
| 			wToken, | 		if err != nil { | ||||||
| 			true, | 			b.Log.Errorf("Could not broadcast via webook for message %#v: %s", msg, err) | ||||||
| 			&discordgo.WebhookParams{ | 			return "", err | ||||||
| 				Content:   msg.Text, | 		} | ||||||
| 				Username:  msg.Username, | 		if msg == nil { | ||||||
| 				AvatarURL: msg.Avatar, | 			return "", nil | ||||||
| 			}) | 		} | ||||||
| 		return "", err | 		return msg.ID, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.Log.Debugf("Broadcasting using token (API)") | 	b.Log.Debugf("Broadcasting using token (API)") | ||||||
| @@ -278,7 +292,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||||
| 			rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength) | 			rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength) | ||||||
| 			if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil { | 			if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil { | ||||||
| 				b.Log.Errorf("Could not send message %#v: %v", rmsg, err) | 				b.Log.Errorf("Could not send message %#v: %s", rmsg, err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// check if we have files to upload (from slack, telegram or mattermost) | 		// check if we have files to upload (from slack, telegram or mattermost) | ||||||
| @@ -301,7 +315,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	return res.ID, err | 	return res.ID, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // useWebhook returns true if we have a webhook defined somewhere | // useWebhook returns true if we have a webhook defined somewhere | ||||||
| @@ -360,8 +374,88 @@ func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (stri | |||||||
| 		} | 		} | ||||||
| 		_, err = b.c.ChannelMessageSendComplex(channelID, &m) | 		_, err = b.c.ChannelMessageSendComplex(channelID, &m) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return "", fmt.Errorf("file upload failed: %#v", err) | 			return "", fmt.Errorf("file upload failed: %s", err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "", nil | 	return "", nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // webhookSend send one or more message via webhook, taking care of file | ||||||
|  | // uploads (from slack, telegram or mattermost). | ||||||
|  | // Returns messageID and error. | ||||||
|  | func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*discordgo.Message, error) { | ||||||
|  | 	var ( | ||||||
|  | 		res *discordgo.Message | ||||||
|  | 		err error | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	// If avatar is unset, check if UseLocalAvatar contains the message's | ||||||
|  | 	// account or protocol, and if so, try to find a local avatar | ||||||
|  | 	if msg.Avatar == "" { | ||||||
|  | 		for _, val := range b.GetStringSlice("UseLocalAvatar") { | ||||||
|  | 			if msg.Protocol == val || msg.Account == val { | ||||||
|  | 				if avatar := b.findAvatar(msg); avatar != "" { | ||||||
|  | 					msg.Avatar = avatar | ||||||
|  | 				} | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// WebhookParams can have either `Content` or `File`. | ||||||
|  |  | ||||||
|  | 	// We can't send empty messages. | ||||||
|  | 	if msg.Text != "" { | ||||||
|  | 		res, err = b.c.WebhookExecute( | ||||||
|  | 			webhookID, | ||||||
|  | 			token, | ||||||
|  | 			true, | ||||||
|  | 			&discordgo.WebhookParams{ | ||||||
|  | 				Content:   msg.Text, | ||||||
|  | 				Username:  msg.Username, | ||||||
|  | 				AvatarURL: msg.Avatar, | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Log.Errorf("Could not send text (%s) for message %#v: %s", msg.Text, msg, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if msg.Extra != nil { | ||||||
|  | 		for _, f := range msg.Extra["file"] { | ||||||
|  | 			fi := f.(config.FileInfo) | ||||||
|  | 			file := discordgo.File{ | ||||||
|  | 				Name:        fi.Name, | ||||||
|  | 				ContentType: "", | ||||||
|  | 				Reader:      bytes.NewReader(*fi.Data), | ||||||
|  | 			} | ||||||
|  | 			content := "" | ||||||
|  | 			if msg.Text == "" { | ||||||
|  | 				content = fi.Comment | ||||||
|  | 			} | ||||||
|  | 			_, e2 := b.c.WebhookExecute( | ||||||
|  | 				webhookID, | ||||||
|  | 				token, | ||||||
|  | 				false, | ||||||
|  | 				&discordgo.WebhookParams{ | ||||||
|  | 					Username:  msg.Username, | ||||||
|  | 					AvatarURL: msg.Avatar, | ||||||
|  | 					File:      &file, | ||||||
|  | 					Content:   content, | ||||||
|  | 				}, | ||||||
|  | 			) | ||||||
|  | 			if e2 != nil { | ||||||
|  | 				b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, e2) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return res, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bdiscord) findAvatar(m *config.Message) string { | ||||||
|  | 	member, err := b.getGuildMemberByNick(m.Username) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	return member.User.AvatarURL("") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -2,20 +2,50 @@ package bdiscord | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
| 	"github.com/bwmarrin/discordgo" | 	"github.com/matterbridge/discordgo" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam | func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam | ||||||
| 	rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete} | 	rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete} | ||||||
| 	rmsg.Channel = b.getChannelName(m.ChannelID) | 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||||
| 	if b.useChannelID { |  | ||||||
| 		rmsg.Channel = "ID:" + m.ChannelID |  | ||||||
| 	} |  | ||||||
| 	b.Log.Debugf("<= Sending message from %s to gateway", b.Account) | 	b.Log.Debugf("<= Sending message from %s to gateway", b.Account) | ||||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||||
| 	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: 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) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) { | ||||||
|  | 	if !b.GetBool("ShowUserTyping") { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Ignore our own typing messages | ||||||
|  | 	if m.UserID == b.userID { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping} | ||||||
|  | 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||||
|  | 	b.Remote <- rmsg | ||||||
|  | } | ||||||
|  |  | ||||||
| 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 | ||||||
| @@ -24,7 +54,10 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat | |||||||
| 	if m.Message.EditedTimestamp != "" { | 	if m.Message.EditedTimestamp != "" { | ||||||
| 		b.Log.Debugf("Sending edit message") | 		b.Log.Debugf("Sending edit message") | ||||||
| 		m.Content += b.GetString("EditSuffix") | 		m.Content += b.GetString("EditSuffix") | ||||||
| 		b.messageCreate(s, (*discordgo.MessageCreate)(m)) | 		msg := &discordgo.MessageCreate{ | ||||||
|  | 			Message: m.Message, | ||||||
|  | 		} | ||||||
|  | 		b.messageCreate(s, msg) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -51,7 +84,6 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
|  |  | ||||||
| 	if m.Content != "" { | 	if m.Content != "" { | ||||||
| 		b.Log.Debugf("== Receiving event %#v", m.Message) | 		b.Log.Debugf("== Receiving event %#v", m.Message) | ||||||
| 		m.Message.Content = b.stripCustomoji(m.Message.Content) |  | ||||||
| 		m.Message.Content = b.replaceChannelMentions(m.Message.Content) | 		m.Message.Content = b.replaceChannelMentions(m.Message.Content) | ||||||
| 		rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c) | 		rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -62,16 +94,13 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
|  |  | ||||||
| 	// set channel name | 	// set channel name | ||||||
| 	rmsg.Channel = b.getChannelName(m.ChannelID) | 	rmsg.Channel = b.getChannelName(m.ChannelID) | ||||||
| 	if b.useChannelID { |  | ||||||
| 		rmsg.Channel = "ID:" + m.ChannelID |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// set username | 	fromWebhook := m.WebhookID != "" | ||||||
| 	if !b.GetBool("UseUserName") { | 	if !fromWebhook && !b.GetBool("UseUserName") { | ||||||
| 		rmsg.Username = b.getNick(m.Author) | 		rmsg.Username = b.getNick(m.Author, m.GuildID) | ||||||
| 	} else { | 	} else { | ||||||
| 		rmsg.Username = m.Author.Username | 		rmsg.Username = m.Author.Username | ||||||
| 		if b.GetBool("UseDiscriminator") { | 		if !fromWebhook && b.GetBool("UseDiscriminator") { | ||||||
| 			rmsg.Username += "#" + m.Author.Discriminator | 			rmsg.Username += "#" + m.Author.Discriminator | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -79,7 +108,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
| 	// if we have embedded content add it to text | 	// if we have embedded content add it to text | ||||||
| 	if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil { | 	if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil { | ||||||
| 		for _, embed := range m.Message.Embeds { | 		for _, embed := range m.Message.Embeds { | ||||||
| 			rmsg.Text = rmsg.Text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n" | 			rmsg.Text += handleEmbed(embed) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -95,6 +124,9 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | |||||||
| 		rmsg.Event = config.EventUserAction | 		rmsg.Event = config.EventUserAction | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Replace emotes | ||||||
|  | 	rmsg.Text = replaceEmotes(rmsg.Text) | ||||||
|  |  | ||||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account) | 	b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account) | ||||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||||
| 	b.Remote <- rmsg | 	b.Remote <- rmsg | ||||||
| @@ -168,3 +200,33 @@ func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRe | |||||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||||
| 	b.Remote <- rmsg | 	b.Remote <- rmsg | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func handleEmbed(embed *discordgo.MessageEmbed) string { | ||||||
|  | 	var t []string | ||||||
|  | 	var result string | ||||||
|  |  | ||||||
|  | 	t = append(t, embed.Title) | ||||||
|  | 	t = append(t, embed.Description) | ||||||
|  | 	t = append(t, embed.URL) | ||||||
|  |  | ||||||
|  | 	i := 0 | ||||||
|  | 	for _, e := range t { | ||||||
|  | 		if e == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		i++ | ||||||
|  | 		if i == 1 { | ||||||
|  | 			result += "embed: " + e | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		result += " - " + e | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if result != "" { | ||||||
|  | 		result += "\n" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								bridge/discord/handlers_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								bridge/discord/handlers_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | package bdiscord | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/matterbridge/discordgo" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestHandleEmbed(t *testing.T) { | ||||||
|  | 	testcases := map[string]struct { | ||||||
|  | 		embed  *discordgo.MessageEmbed | ||||||
|  | 		result string | ||||||
|  | 	}{ | ||||||
|  | 		"allempty": { | ||||||
|  | 			embed:  &discordgo.MessageEmbed{}, | ||||||
|  | 			result: "", | ||||||
|  | 		}, | ||||||
|  | 		"one": { | ||||||
|  | 			embed: &discordgo.MessageEmbed{ | ||||||
|  | 				Title: "blah", | ||||||
|  | 			}, | ||||||
|  | 			result: "embed: blah\n", | ||||||
|  | 		}, | ||||||
|  | 		"two": { | ||||||
|  | 			embed: &discordgo.MessageEmbed{ | ||||||
|  | 				Title:       "blah", | ||||||
|  | 				Description: "blah2", | ||||||
|  | 			}, | ||||||
|  | 			result: "embed: blah - blah2\n", | ||||||
|  | 		}, | ||||||
|  | 		"three": { | ||||||
|  | 			embed: &discordgo.MessageEmbed{ | ||||||
|  | 				Title:       "blah", | ||||||
|  | 				Description: "blah2", | ||||||
|  | 				URL:         "blah3", | ||||||
|  | 			}, | ||||||
|  | 			result: "embed: blah - blah2 - blah3\n", | ||||||
|  | 		}, | ||||||
|  | 		"twob": { | ||||||
|  | 			embed: &discordgo.MessageEmbed{ | ||||||
|  | 				Description: "blah2", | ||||||
|  | 				URL:         "blah3", | ||||||
|  | 			}, | ||||||
|  | 			result: "embed: blah2 - blah3\n", | ||||||
|  | 		}, | ||||||
|  | 		"oneb": { | ||||||
|  | 			embed: &discordgo.MessageEmbed{ | ||||||
|  | 				URL: "blah3", | ||||||
|  | 			}, | ||||||
|  | 			result: "embed: blah3\n", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for name, tc := range testcases { | ||||||
|  | 		assert.Equalf(t, tc.result, handleEmbed(tc.embed), "Testcases %s", name) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -6,10 +6,10 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"unicode" | 	"unicode" | ||||||
|  |  | ||||||
| 	"github.com/bwmarrin/discordgo" | 	"github.com/matterbridge/discordgo" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (b *Bdiscord) getNick(user *discordgo.User) string { | func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string { | ||||||
| 	b.membersMutex.RLock() | 	b.membersMutex.RLock() | ||||||
| 	defer b.membersMutex.RUnlock() | 	defer b.membersMutex.RUnlock() | ||||||
|  |  | ||||||
| @@ -23,9 +23,9 @@ func (b *Bdiscord) getNick(user *discordgo.User) string { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// If we didn't find nick, search for it. | 	// If we didn't find nick, search for it. | ||||||
| 	member, err := b.c.GuildMember(b.guildID, user.ID) | 	member, err := b.c.GuildMember(guildID, user.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		b.Log.Warnf("Failed to fetch information for member %#v: %#v", user, err) | 		b.Log.Warnf("Failed to fetch information for member %#v on guild %#v: %s", user, guildID, err) | ||||||
| 		return user.Username | 		return user.Username | ||||||
| 	} else if member == nil { | 	} else if member == nil { | ||||||
| 		b.Log.Warnf("Got no information for member %#v", user) | 		b.Log.Warnf("Got no information for member %#v", user) | ||||||
| @@ -51,6 +51,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,40 +62,92 @@ 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 _, c := range b.channelInfoMap { | ||||||
|  | 		if c.Name == "ID:"+id { | ||||||
|  | 			// if we have ID: specified in our gateway configuration return this | ||||||
|  | 			return c.Name | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	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]+>") | ||||||
| 	emojiRE          = regexp.MustCompile("<(:.*?:)[0-9]+>") |  | ||||||
| 	userMentionRE    = regexp.MustCompile("@[^@\n]{1,32}") | 	userMentionRE    = regexp.MustCompile("@[^@\n]{1,32}") | ||||||
|  | 	emoteRE          = regexp.MustCompile(`<a?(:\w+:)\d+>`) | ||||||
| ) | ) | ||||||
|  |  | ||||||
| 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" | ||||||
| @@ -128,13 +183,13 @@ func (b *Bdiscord) replaceUserMentions(text string) string { | |||||||
| 	return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc) | 	return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bdiscord) stripCustomoji(text string) string { | func replaceEmotes(text string) string { | ||||||
| 	return emojiRE.ReplaceAllString(text, `$1`) | 	return emoteRE.ReplaceAllString(text, "$1") | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bdiscord) replaceAction(text string) (string, bool) { | func (b *Bdiscord) replaceAction(text string) (string, bool) { | ||||||
| 	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") { | 	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") { | ||||||
| 		return text[1:], true | 		return text[1 : len(text)-1], true | ||||||
| 	} | 	} | ||||||
| 	return text, false | 	return text, false | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package helper | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"image/png" | ||||||
| 	"io" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| @@ -10,9 +11,13 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/webp" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/gomarkdown/markdown" | ||||||
|  | 	"github.com/gomarkdown/markdown/html" | ||||||
|  | 	"github.com/gomarkdown/markdown/parser" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"gitlab.com/golang-commonmark/markdown" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // DownloadFile downloads the given non-authenticated URL. | // DownloadFile downloads the given non-authenticated URL. | ||||||
| @@ -173,7 +178,32 @@ func ClipMessage(text string, length int) string { | |||||||
| 	return text | 	return text | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ParseMarkdown takes in an input string as markdown and parses it to html | ||||||
| func ParseMarkdown(input string) string { | func ParseMarkdown(input string) string { | ||||||
| 	md := markdown.New(markdown.XHTMLOutput(true), markdown.Breaks(true)) | 	extensions := parser.HardLineBreak | parser.NoIntraEmphasis | ||||||
| 	return (md.RenderToString([]byte(input))) | 	markdownParser := parser.NewWithExtensions(extensions) | ||||||
|  | 	renderer := html.NewRenderer(html.RendererOptions{ | ||||||
|  | 		Flags: 0, | ||||||
|  | 	}) | ||||||
|  | 	parsedMarkdown := markdown.ToHTML([]byte(input), markdownParser, renderer) | ||||||
|  | 	res := string(parsedMarkdown) | ||||||
|  | 	res = strings.TrimPrefix(res, "<p>") | ||||||
|  | 	res = strings.TrimSuffix(res, "</p>\n") | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ConvertWebPToPNG convert input data (which should be WebP format to PNG format) | ||||||
|  | func ConvertWebPToPNG(data *[]byte) error { | ||||||
|  | 	r := bytes.NewReader(*data) | ||||||
|  | 	m, err := webp.Decode(r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var output []byte | ||||||
|  | 	w := bytes.NewBuffer(output) | ||||||
|  | 	if err := png.Encode(w, m); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*data = w.Bytes() | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package helper | package helper | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @@ -103,3 +105,22 @@ func TestGetSubLines(t *testing.T) { | |||||||
| 		assert.Equalf(t, testcase.nonSplitOutput, nonSplitLines, "'%s' testcase should give expected lines without splitting.", testname) | 		assert.Equalf(t, testcase.nonSplitOutput, nonSplitLines, "'%s' testcase should give expected lines without splitting.", testname) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestConvertWebPToPNG(t *testing.T) { | ||||||
|  | 	if os.Getenv("LOCAL_TEST") == "" { | ||||||
|  | 		t.Skip() | ||||||
|  | 	} | ||||||
|  | 	input, err := ioutil.ReadFile("test.webp") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fail() | ||||||
|  | 	} | ||||||
|  | 	d := &input | ||||||
|  | 	err = ConvertWebPToPNG(d) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fail() | ||||||
|  | 	} | ||||||
|  | 	err = ioutil.WriteFile("test.png", *d, 0644) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fail() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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 | ||||||
| @@ -166,12 +167,8 @@ func (b *Birc) Send(msg config.Message) (string, error) { | |||||||
| 			return "", nil | 			return "", nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		b.Local <- config.Message{ | 		msg.Text = msgLines[i] | ||||||
| 			Text:     msgLines[i], | 		b.Local <- msg | ||||||
| 			Username: msg.Username, |  | ||||||
| 			Channel:  msg.Channel, |  | ||||||
| 			Event:    msg.Event, |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return "", nil | 	return "", nil | ||||||
| } | } | ||||||
| @@ -231,7 +228,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 | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										59
									
								
								bridge/keybase/handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								bridge/keybase/handlers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | package bkeybase | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (b *Bkeybase) handleKeybase() { | ||||||
|  | 	sub, err := b.kbc.ListenForNewTextMessages() | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("Error listening: %s", err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		for { | ||||||
|  | 			msg, err := sub.Read() | ||||||
|  | 			if err != nil { | ||||||
|  | 				b.Log.Errorf("failed to read message: %s", err.Error()) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if msg.Message.Content.TypeName != "text" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if msg.Message.Sender.Username == b.kbc.GetUsername() { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			b.handleMessage(msg.Message) | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bkeybase) handleMessage(msg chat1.MsgSummary) { | ||||||
|  | 	b.Log.Debugf("== Receiving event: %#v", msg) | ||||||
|  | 	if msg.Channel.TopicName != b.channel || msg.Channel.Name != b.team { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if msg.Sender.Username != b.kbc.GetUsername() { | ||||||
|  |  | ||||||
|  | 		// TODO download avatar | ||||||
|  |  | ||||||
|  | 		// Create our message | ||||||
|  | 		rmsg := config.Message{Username: msg.Sender.Username, Text: msg.Content.Text.Body, UserID: string(msg.Sender.Uid), Channel: msg.Channel.TopicName, ID: strconv.Itoa(int(msg.Id)), Account: b.Account} | ||||||
|  |  | ||||||
|  | 		// Text must be a string | ||||||
|  | 		if msg.Content.TypeName != "text" { | ||||||
|  | 			b.Log.Errorf("message is not text") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		b.Log.Debugf("<= Sending message from %s on %s to gateway", msg.Sender.Username, msg.Channel.Name) | ||||||
|  | 		b.Remote <- rmsg | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								bridge/keybase/keybase.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								bridge/keybase/keybase.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | package bkeybase | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"github.com/42wim/matterbridge/bridge" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/keybase/go-keybase-chat-bot/kbchat" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Bkeybase bridge structure | ||||||
|  | type Bkeybase struct { | ||||||
|  | 	kbc     *kbchat.API | ||||||
|  | 	user    string | ||||||
|  | 	channel string | ||||||
|  | 	team    string | ||||||
|  | 	*bridge.Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New initializes Bkeybase object and sets team | ||||||
|  | func New(cfg *bridge.Config) bridge.Bridger { | ||||||
|  | 	b := &Bkeybase{Config: cfg} | ||||||
|  | 	b.team = b.Config.GetString("Team") | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Connect starts keybase API and listener loop | ||||||
|  | func (b *Bkeybase) Connect() error { | ||||||
|  | 	var err error | ||||||
|  | 	b.Log.Infof("Connecting %s", b.GetString("Team")) | ||||||
|  |  | ||||||
|  | 	// use default keybase location (`keybase`) | ||||||
|  | 	b.kbc, err = kbchat.Start(kbchat.RunOptions{}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	b.user = b.kbc.GetUsername() | ||||||
|  | 	b.Log.Info("Connection succeeded") | ||||||
|  | 	go b.handleKeybase() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Disconnect doesn't do anything for now | ||||||
|  | func (b *Bkeybase) Disconnect() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // JoinChannel sets channel name in struct | ||||||
|  | func (b *Bkeybase) JoinChannel(channel config.ChannelInfo) error { | ||||||
|  | 	if _, err := b.kbc.JoinChannel(b.team, channel.Name); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	b.channel = channel.Name | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Send receives bridge messages and sends them to Keybase chat room | ||||||
|  | func (b *Bkeybase) Send(msg config.Message) (string, error) { | ||||||
|  | 	b.Log.Debugf("=> Receiving %#v", msg) | ||||||
|  |  | ||||||
|  | 	// Handle /me events | ||||||
|  | 	if msg.Event == config.EventUserAction { | ||||||
|  | 		msg.Text = "_" + msg.Text + "_" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Delete message if we have an ID | ||||||
|  | 	// Delete message not supported by keybase go library yet | ||||||
|  |  | ||||||
|  | 	// Edit message if we have an ID | ||||||
|  | 	// kbchat lib does not support message editing yet | ||||||
|  |  | ||||||
|  | 	if len(msg.Extra["file"]) > 0 { | ||||||
|  | 		// Upload a file | ||||||
|  | 		dir, err := ioutil.TempDir("", "matterbridge") | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", err | ||||||
|  | 		} | ||||||
|  | 		defer os.RemoveAll(dir) | ||||||
|  |  | ||||||
|  | 		for _, f := range msg.Extra["file"] { | ||||||
|  | 			fname := f.(config.FileInfo).Name | ||||||
|  | 			fdata := *f.(config.FileInfo).Data | ||||||
|  | 			fcaption := f.(config.FileInfo).Comment | ||||||
|  | 			fpath := filepath.Join(dir, fname) | ||||||
|  |  | ||||||
|  | 			if err = ioutil.WriteFile(fpath, fdata, 0600); err != nil { | ||||||
|  | 				return "", err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			_, _ = b.kbc.SendAttachmentByTeam(b.team, &b.channel, fpath, fcaption) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return "", nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Send regular message | ||||||
|  | 	text := msg.Username + msg.Text | ||||||
|  | 	resp, err := b.kbc.SendMessageByTeamName(b.team, &b.channel, text) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return strconv.Itoa(int(*resp.Result.MessageID)), err | ||||||
|  | } | ||||||
| @@ -172,10 +172,15 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// TODO download avatar |  | ||||||
|  |  | ||||||
| 		// Create our message | 		// Create our message | ||||||
| 		rmsg := config.Message{Username: ev.Sender[1:], Channel: channel, Account: b.Account, UserID: ev.Sender, ID: ev.ID} | 		rmsg := config.Message{ | ||||||
|  | 			Username: ev.Sender[1:], | ||||||
|  | 			Channel:  channel, | ||||||
|  | 			Account:  b.Account, | ||||||
|  | 			UserID:   ev.Sender, | ||||||
|  | 			ID:       ev.ID, | ||||||
|  | 			Avatar:   b.getAvatarURL(ev.Sender), | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// Text must be a string | 		// Text must be a string | ||||||
| 		if rmsg.Text, ok = ev.Content["body"].(string); !ok { | 		if rmsg.Text, ok = ev.Content["body"].(string); !ok { | ||||||
| @@ -291,7 +296,8 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf | |||||||
| 	content := bytes.NewReader(*fi.Data) | 	content := bytes.NewReader(*fi.Data) | ||||||
| 	sp := strings.Split(fi.Name, ".") | 	sp := strings.Split(fi.Name, ".") | ||||||
| 	mtype := mime.TypeByExtension("." + sp[len(sp)-1]) | 	mtype := mime.TypeByExtension("." + sp[len(sp)-1]) | ||||||
| 	if !strings.Contains(mtype, "image") && !strings.Contains(mtype, "video") { | 	if !(strings.Contains(mtype, "image") || strings.Contains(mtype, "video") || | ||||||
|  | 		strings.Contains(mtype, "application") || strings.Contains(mtype, "audio")) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if fi.Comment != "" { | 	if fi.Comment != "" { | ||||||
| @@ -326,6 +332,18 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			b.Log.Errorf("sendImage failed: %#v", err) | 			b.Log.Errorf("sendImage failed: %#v", err) | ||||||
| 		} | 		} | ||||||
|  | 	case strings.Contains(mtype, "application"): | ||||||
|  | 		b.Log.Debugf("sendFile %s", res.ContentURI) | ||||||
|  | 		_, err = b.mc.SendFile(channel, fi.Name, res.ContentURI, mtype, uint(len(*fi.Data))) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Log.Errorf("sendFile failed: %#v", err) | ||||||
|  | 		} | ||||||
|  | 	case strings.Contains(mtype, "audio"): | ||||||
|  | 		b.Log.Debugf("sendAudio %s", res.ContentURI) | ||||||
|  | 		_, err = b.mc.SendAudio(channel, fi.Name, res.ContentURI, mtype, uint(len(*fi.Data))) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Log.Errorf("sendAudio failed: %#v", err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	b.Log.Debugf("result: %#v", res) | 	b.Log.Debugf("result: %#v", res) | ||||||
| } | } | ||||||
| @@ -345,3 +363,15 @@ func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool { | |||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // getAvatarURL returns the avatar URL of the specified sender | ||||||
|  | func (b *Bmatrix) getAvatarURL(sender string) string { | ||||||
|  | 	mxcURL, err := b.mc.GetSenderAvatarURL(sender) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("getAvatarURL failed: %s", err) | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	url := strings.ReplaceAll(mxcURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/") | ||||||
|  | 	url += "?width=37&height=37&method=crop" | ||||||
|  | 	return url | ||||||
|  | } | ||||||
|   | |||||||
| @@ -66,6 +66,10 @@ func (b *Bmattermost) handleMatter() { | |||||||
| 		} else { | 		} else { | ||||||
| 			b.Log.Debugf("Choosing login/password based receiving") | 			b.Log.Debugf("Choosing login/password based receiving") | ||||||
| 		} | 		} | ||||||
|  | 		// if for some reason we only want to sent stuff to mattermost but not receive, return | ||||||
|  | 		if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") != "" && b.GetString("Token") == "" && b.GetString("Login") == "" { | ||||||
|  | 			b.Log.Debugf("No WebhookBindAddress specified, only WebhookURL. You will not receive messages from mattermost, only sending is possible.") | ||||||
|  | 		} | ||||||
| 		go b.handleMatterClient(messages) | 		go b.handleMatterClient(messages) | ||||||
| 	} | 	} | ||||||
| 	var ok bool | 	var ok bool | ||||||
|   | |||||||
| @@ -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) { | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								bridge/msteams/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								bridge/msteams/handler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | package bmsteams | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/helper" | ||||||
|  |  | ||||||
|  | 	msgraph "github.com/yaegashi/msgraph.go/beta" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) findFile(weburl string) (string, error) { | ||||||
|  | 	itemRB, err := b.gc.GetDriveItemByURL(b.ctx, weburl) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	itemRB.Workbook().Worksheets() | ||||||
|  | 	b.gc.Workbooks() | ||||||
|  | 	item, err := itemRB.Request().Get(b.ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	if url, ok := item.GetAdditionalData("@microsoft.graph.downloadUrl"); ok { | ||||||
|  | 		return url.(string), nil | ||||||
|  | 	} | ||||||
|  | 	return "", nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handleDownloadFile handles file download | ||||||
|  | func (b *Bmsteams) handleDownloadFile(rmsg *config.Message, filename, weburl string) error { | ||||||
|  | 	realURL, err := b.findFile(weburl) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// Actually download the file. | ||||||
|  | 	data, err := helper.DownloadFile(realURL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("download %s failed %#v", weburl, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If a comment is attached to the file(s) it is in the 'Text' field of the teams messge event | ||||||
|  | 	// and should be added as comment to only one of the files. We reset the 'Text' field to ensure | ||||||
|  | 	// that the comment is not duplicated. | ||||||
|  | 	comment := rmsg.Text | ||||||
|  | 	rmsg.Text = "" | ||||||
|  | 	helper.HandleDownloadData(b.Log, rmsg, filename, comment, weburl, data, b.General) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) handleAttachments(rmsg *config.Message, msg msgraph.ChatMessage) { | ||||||
|  | 	for _, a := range msg.Attachments { | ||||||
|  | 		//remove the attachment tags from the text | ||||||
|  | 		rmsg.Text = attachRE.ReplaceAllString(rmsg.Text, "") | ||||||
|  |  | ||||||
|  | 		//handle a code snippet (code block) | ||||||
|  | 		if *a.ContentType == "application/vnd.microsoft.card.codesnippet" { | ||||||
|  | 			b.handleCodeSnippet(rmsg, a) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		//handle the download | ||||||
|  | 		err := b.handleDownloadFile(rmsg, *a.Name, *a.ContentURL) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Log.Errorf("download of %s failed: %s", *a.Name, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type AttachContent struct { | ||||||
|  | 	Language       string `json:"language"` | ||||||
|  | 	CodeSnippetURL string `json:"codeSnippetUrl"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) handleCodeSnippet(rmsg *config.Message, attach msgraph.ChatMessageAttachment) { | ||||||
|  | 	var content AttachContent | ||||||
|  | 	err := json.Unmarshal([]byte(*attach.Content), &content) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("unmarshal codesnippet failed: %s", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	s := strings.Split(content.CodeSnippetURL, "/") | ||||||
|  | 	if len(s) != 13 { | ||||||
|  | 		b.Log.Errorf("codesnippetUrl has unexpected size: %s", content.CodeSnippetURL) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	resp, err := b.gc.Teams().Request().Client().Get(content.CodeSnippetURL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("retrieving snippet content failed:%s", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  | 	res, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("reading snippet data failed: %s", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	rmsg.Text = rmsg.Text + "\n```" + content.Language + "\n" + string(res) + "\n```\n" | ||||||
|  | } | ||||||
							
								
								
									
										205
									
								
								bridge/msteams/msteams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								bridge/msteams/msteams.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | |||||||
|  | package bmsteams | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/42wim/matterbridge/bridge" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  |  | ||||||
|  | 	//	"github.com/davecgh/go-spew/spew" | ||||||
|  | 	"github.com/matterbridge/msgraph.go/msauth" | ||||||
|  | 	"github.com/mattn/godown" | ||||||
|  | 	msgraph "github.com/yaegashi/msgraph.go/beta" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/oauth2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var defaultScopes = []string{"openid", "profile", "offline_access", "Group.Read.All", "Group.ReadWrite.All"} | ||||||
|  | var attachRE = regexp.MustCompile(`<attachment id=.*?attachment>`) | ||||||
|  |  | ||||||
|  | type Bmsteams struct { | ||||||
|  | 	gc    *msgraph.GraphServiceRequestBuilder | ||||||
|  | 	ctx   context.Context | ||||||
|  | 	botID string | ||||||
|  | 	*bridge.Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func New(cfg *bridge.Config) bridge.Bridger { | ||||||
|  | 	return &Bmsteams{Config: cfg} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) Connect() error { | ||||||
|  | 	tokenCachePath := b.GetString("sessionFile") | ||||||
|  | 	if tokenCachePath == "" { | ||||||
|  | 		tokenCachePath = "msteams_session.json" | ||||||
|  | 	} | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	m := msauth.NewManager() | ||||||
|  | 	m.LoadFile(tokenCachePath) //nolint:errcheck | ||||||
|  | 	ts, err := m.DeviceAuthorizationGrant(ctx, b.GetString("TenantID"), b.GetString("ClientID"), defaultScopes, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = m.SaveFile(tokenCachePath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("Couldn't save sessionfile in %s: %s", tokenCachePath, err) | ||||||
|  | 	} | ||||||
|  | 	// make file readable only for matterbridge user | ||||||
|  | 	err = os.Chmod(tokenCachePath, 0600) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("Couldn't change permissions for %s: %s", tokenCachePath, err) | ||||||
|  | 	} | ||||||
|  | 	httpClient := oauth2.NewClient(ctx, ts) | ||||||
|  | 	graphClient := msgraph.NewClient(httpClient) | ||||||
|  | 	b.gc = graphClient | ||||||
|  | 	b.ctx = ctx | ||||||
|  |  | ||||||
|  | 	err = b.setBotID() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	b.Log.Info("Connection succeeded") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) Disconnect() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error { | ||||||
|  | 	go b.poll(channel.Name) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) Send(msg config.Message) (string, error) { | ||||||
|  | 	b.Log.Debugf("=> Receiving %#v", msg) | ||||||
|  | 	if msg.ParentID != "" && msg.ParentID != "msg-parent-not-found" { | ||||||
|  | 		return b.sendReply(msg) | ||||||
|  | 	} | ||||||
|  | 	if msg.ParentID == "msg-parent-not-found" { | ||||||
|  | 		msg.ParentID = "" | ||||||
|  | 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||||
|  | 	} | ||||||
|  | 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().Request() | ||||||
|  | 	text := msg.Username + msg.Text | ||||||
|  | 	content := &msgraph.ItemBody{Content: &text} | ||||||
|  | 	rmsg := &msgraph.ChatMessage{Body: content} | ||||||
|  | 	res, err := ct.Add(b.ctx, rmsg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return *res.ID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) sendReply(msg config.Message) (string, error) { | ||||||
|  | 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().ID(msg.ParentID).Replies().Request() | ||||||
|  | 	// Handle prefix hint for unthreaded messages. | ||||||
|  |  | ||||||
|  | 	text := msg.Username + msg.Text | ||||||
|  | 	content := &msgraph.ItemBody{Content: &text} | ||||||
|  | 	rmsg := &msgraph.ChatMessage{Body: content} | ||||||
|  | 	res, err := ct.Add(b.ctx, rmsg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return *res.ID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) { | ||||||
|  | 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(channel).Messages().Request() | ||||||
|  | 	rct, err := ct.Get(b.ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	b.Log.Debugf("got %#v messages", len(rct)) | ||||||
|  | 	return rct, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //nolint:gocognit | ||||||
|  | func (b *Bmsteams) poll(channelName string) { | ||||||
|  | 	msgmap := make(map[string]time.Time) | ||||||
|  | 	b.Log.Debug("getting initial messages") | ||||||
|  | 	res, err := b.getMessages(channelName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	for _, msg := range res { | ||||||
|  | 		msgmap[*msg.ID] = *msg.CreatedDateTime | ||||||
|  | 		if msg.LastModifiedDateTime != nil { | ||||||
|  | 			msgmap[*msg.ID] = *msg.LastModifiedDateTime | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	time.Sleep(time.Second * 5) | ||||||
|  | 	b.Log.Debug("polling for messages") | ||||||
|  | 	for { | ||||||
|  | 		res, err := b.getMessages(channelName) | ||||||
|  | 		if err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 		for i := len(res) - 1; i >= 0; i-- { | ||||||
|  | 			msg := res[i] | ||||||
|  | 			if mtime, ok := msgmap[*msg.ID]; ok { | ||||||
|  | 				if mtime == *msg.CreatedDateTime && msg.LastModifiedDateTime == nil { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				if msg.LastModifiedDateTime != nil && mtime == *msg.LastModifiedDateTime { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if *msg.From.User.ID == b.botID { | ||||||
|  | 				b.Log.Debug("skipping own message") | ||||||
|  | 				msgmap[*msg.ID] = *msg.CreatedDateTime | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			msgmap[*msg.ID] = *msg.CreatedDateTime | ||||||
|  | 			if msg.LastModifiedDateTime != nil { | ||||||
|  | 				msgmap[*msg.ID] = *msg.LastModifiedDateTime | ||||||
|  | 			} | ||||||
|  | 			b.Log.Debugf("<= Sending message from %s on %s to gateway", *msg.From.User.DisplayName, b.Account) | ||||||
|  | 			text := b.convertToMD(*msg.Body.Content) | ||||||
|  | 			rmsg := config.Message{ | ||||||
|  | 				Username: *msg.From.User.DisplayName, | ||||||
|  | 				Text:     text, | ||||||
|  | 				Channel:  channelName, | ||||||
|  | 				Account:  b.Account, | ||||||
|  | 				Avatar:   "", | ||||||
|  | 				UserID:   *msg.From.User.ID, | ||||||
|  | 				ID:       *msg.ID, | ||||||
|  | 				Extra:    make(map[string][]interface{}), | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			b.handleAttachments(&rmsg, msg) | ||||||
|  | 			b.Log.Debugf("<= Message is %#v", rmsg) | ||||||
|  | 			b.Remote <- rmsg | ||||||
|  | 		} | ||||||
|  | 		time.Sleep(time.Second * 5) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) setBotID() error { | ||||||
|  | 	req := b.gc.Me().Request() | ||||||
|  | 	r, err := req.Get(b.ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	b.botID = *r.ID | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bmsteams) convertToMD(text string) string { | ||||||
|  | 	if !strings.Contains(text, "<div>") { | ||||||
|  | 		return text | ||||||
|  | 	} | ||||||
|  | 	var sb strings.Builder | ||||||
|  | 	err := godown.Convert(&sb, strings.NewReader(text), nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("Couldn't convert message to markdown %s", text) | ||||||
|  | 		return text | ||||||
|  | 	} | ||||||
|  | 	return sb.String() | ||||||
|  | } | ||||||
| @@ -40,6 +40,11 @@ func (b *Brocketchat) handleRocketHook(messages chan *config.Message) { | |||||||
|  |  | ||||||
| func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | ||||||
| 	for message := range b.messageChan { | 	for message := range b.messageChan { | ||||||
|  | 		// skip messages with same ID, apparently messages get duplicated for an unknown reason | ||||||
|  | 		if _, ok := b.cache.Get(message.ID); ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		b.cache.Add(message.ID, true) | ||||||
| 		b.Log.Debugf("message %#v", message) | 		b.Log.Debugf("message %#v", message) | ||||||
| 		m := message | 		m := message | ||||||
| 		if b.skipMessage(&m) { | 		if b.skipMessage(&m) { | ||||||
|   | |||||||
| @@ -58,6 +58,9 @@ func (b *Brocketchat) doConnectWebhookURL() error { | |||||||
| func (b *Brocketchat) apiLogin() error { | func (b *Brocketchat) apiLogin() error { | ||||||
| 	b.Log.Debugf("handling apiLogin()") | 	b.Log.Debugf("handling apiLogin()") | ||||||
| 	credentials := &models.UserCredentials{Email: b.GetString("login"), Password: b.GetString("password")} | 	credentials := &models.UserCredentials{Email: b.GetString("login"), Password: b.GetString("password")} | ||||||
|  | 	if b.GetString("Token") != "" { | ||||||
|  | 		credentials = &models.UserCredentials{ID: b.GetString("Login"), Token: b.GetString("Token")} | ||||||
|  | 	} | ||||||
| 	myURL, err := url.Parse(b.GetString("server")) | 	myURL, err := url.Parse(b.GetString("server")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -95,7 +98,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 | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package brocketchat | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| @@ -9,16 +10,18 @@ import ( | |||||||
| 	"github.com/42wim/matterbridge/bridge/helper" | 	"github.com/42wim/matterbridge/bridge/helper" | ||||||
| 	"github.com/42wim/matterbridge/hook/rockethook" | 	"github.com/42wim/matterbridge/hook/rockethook" | ||||||
| 	"github.com/42wim/matterbridge/matterhook" | 	"github.com/42wim/matterbridge/matterhook" | ||||||
|  | 	lru "github.com/hashicorp/golang-lru" | ||||||
| 	"github.com/matterbridge/Rocket.Chat.Go.SDK/models" | 	"github.com/matterbridge/Rocket.Chat.Go.SDK/models" | ||||||
| 	"github.com/matterbridge/Rocket.Chat.Go.SDK/realtime" | 	"github.com/matterbridge/Rocket.Chat.Go.SDK/realtime" | ||||||
| 	"github.com/matterbridge/Rocket.Chat.Go.SDK/rest" | 	"github.com/matterbridge/Rocket.Chat.Go.SDK/rest" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Brocketchat struct { | type Brocketchat struct { | ||||||
| 	mh *matterhook.Client | 	mh    *matterhook.Client | ||||||
| 	rh *rockethook.Client | 	rh    *rockethook.Client | ||||||
| 	c  *realtime.Client | 	c     *realtime.Client | ||||||
| 	r  *rest.Client | 	r     *rest.Client | ||||||
|  | 	cache *lru.Cache | ||||||
| 	*bridge.Config | 	*bridge.Config | ||||||
| 	messageChan chan models.Message | 	messageChan chan models.Message | ||||||
| 	channelMap  map[string]string | 	channelMap  map[string]string | ||||||
| @@ -27,9 +30,16 @@ type Brocketchat struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| func New(cfg *bridge.Config) bridge.Bridger { | func New(cfg *bridge.Config) bridge.Bridger { | ||||||
| 	b := &Brocketchat{Config: cfg} | 	newCache, err := lru.New(100) | ||||||
| 	b.messageChan = make(chan models.Message) | 	if err != nil { | ||||||
| 	b.channelMap = make(map[string]string) | 		cfg.Log.Fatalf("Could not create LRU cache for rocketchat bridge: %v", err) | ||||||
|  | 	} | ||||||
|  | 	b := &Brocketchat{ | ||||||
|  | 		Config:      cfg, | ||||||
|  | 		messageChan: make(chan models.Message), | ||||||
|  | 		channelMap:  make(map[string]string), | ||||||
|  | 		cache:       newCache, | ||||||
|  | 	} | ||||||
| 	b.Log.Debugf("enabling rocketchat") | 	b.Log.Debugf("enabling rocketchat") | ||||||
| 	return b | 	return b | ||||||
| } | } | ||||||
| @@ -76,14 +86,14 @@ func (b *Brocketchat) JoinChannel(channel config.ChannelInfo) error { | |||||||
| 	if b.c == nil { | 	if b.c == nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	id, err := b.c.GetChannelId(channel.Name) | 	id, err := b.c.GetChannelId(strings.TrimPrefix(channel.Name, "#")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	b.Lock() | 	b.Lock() | ||||||
| 	b.channelMap[id] = channel.Name | 	b.channelMap[id] = channel.Name | ||||||
| 	b.Unlock() | 	b.Unlock() | ||||||
| 	mychannel := &models.Channel{ID: id, Name: channel.Name} | 	mychannel := &models.Channel{ID: id, Name: strings.TrimPrefix(channel.Name, "#")} | ||||||
| 	if err := b.c.JoinChannel(id); err != nil { | 	if err := b.c.JoinChannel(id); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -94,8 +104,15 @@ func (b *Brocketchat) JoinChannel(channel config.ChannelInfo) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Brocketchat) Send(msg config.Message) (string, error) { | func (b *Brocketchat) Send(msg config.Message) (string, error) { | ||||||
|  | 	// strip the # if people has set this | ||||||
|  | 	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 == "" { | ||||||
| @@ -122,6 +139,8 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) { | |||||||
| 	// 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) { | ||||||
|  | 			// strip the # if people has set this | ||||||
|  | 			rmsg.Channel = strings.TrimPrefix(rmsg.Channel, "#") | ||||||
| 			smsg := &models.Message{ | 			smsg := &models.Message{ | ||||||
| 				RoomID: b.getChannelID(rmsg.Channel), | 				RoomID: b.getChannelID(rmsg.Channel), | ||||||
| 				Msg:    rmsg.Username + rmsg.Text, | 				Msg:    rmsg.Username + rmsg.Text, | ||||||
|   | |||||||
| @@ -1,15 +1,19 @@ | |||||||
| package bslack | package bslack | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html" | 	"html" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"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/nlopes/slack" | 	"github.com/slack-go/slack" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // ErrEventIgnored is for events that should be ignored | ||||||
|  | var ErrEventIgnored = errors.New("this event message should ignored") | ||||||
|  |  | ||||||
| func (b *Bslack) handleSlack() { | func (b *Bslack) handleSlack() { | ||||||
| 	messages := make(chan *config.Message) | 	messages := make(chan *config.Message) | ||||||
| 	if b.GetString(incomingWebhookConfig) != "" { | 	if b.GetString(incomingWebhookConfig) != "" { | ||||||
| @@ -22,20 +26,21 @@ 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 = b.replaceb0rkedMarkDown(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.getAvatar(message.UserID) |  | ||||||
|  |  | ||||||
| 		b.Log.Debugf("<= Message is %#v", message) | 		b.Log.Debugf("<= Message is %#v", message) | ||||||
| 		b.Remote <- *message | 		b.Remote <- *message | ||||||
| 	} | 	} | ||||||
| @@ -43,7 +48,7 @@ func (b *Bslack) handleSlack() { | |||||||
|  |  | ||||||
| func (b *Bslack) handleSlackClient(messages chan *config.Message) { | func (b *Bslack) handleSlackClient(messages chan *config.Message) { | ||||||
| 	for msg := range b.rtm.IncomingEvents { | 	for msg := range b.rtm.IncomingEvents { | ||||||
| 		if msg.Type != sUserTyping && msg.Type != sLatencyReport { | 		if msg.Type != sUserTyping && msg.Type != sHello && msg.Type != sLatencyReport { | ||||||
| 			b.Log.Debugf("== Receiving event %#v", msg.Data) | 			b.Log.Debugf("== Receiving event %#v", msg.Data) | ||||||
| 		} | 		} | ||||||
| 		switch ev := msg.Data.(type) { | 		switch ev := msg.Data.(type) { | ||||||
| @@ -52,7 +57,9 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) { | |||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			rmsg, err := b.handleTypingEvent(ev) | 			rmsg, err := b.handleTypingEvent(ev) | ||||||
| 			if err != nil { | 			if err == ErrEventIgnored { | ||||||
|  | 				continue | ||||||
|  | 			} else if err != nil { | ||||||
| 				b.Log.Errorf("%#v", err) | 				b.Log.Errorf("%#v", err) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| @@ -75,21 +82,18 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) { | |||||||
| 			// When we join a channel we update the full list of users as | 			// When we join a channel we update the full list of users as | ||||||
| 			// well as the information for the channel that we joined as this | 			// well as the information for the channel that we joined as this | ||||||
| 			// should now tell that we are a member of it. | 			// should now tell that we are a member of it. | ||||||
| 			b.channelsMutex.Lock() | 			b.channels.registerChannel(ev.Channel) | ||||||
| 			b.channelsByID[ev.Channel.ID] = &ev.Channel |  | ||||||
| 			b.channelsByName[ev.Channel.Name] = &ev.Channel |  | ||||||
| 			b.channelsMutex.Unlock() |  | ||||||
| 		case *slack.ConnectedEvent: | 		case *slack.ConnectedEvent: | ||||||
| 			b.si = ev.Info | 			b.si = ev.Info | ||||||
| 			b.populateChannels(true) | 			b.channels.populateChannels(true) | ||||||
| 			b.populateUsers(true) | 			b.users.populateUsers(true) | ||||||
| 		case *slack.InvalidAuthEvent: | 		case *slack.InvalidAuthEvent: | ||||||
| 			b.Log.Fatalf("Invalid Token %#v", ev) | 			b.Log.Fatalf("Invalid Token %#v", ev) | ||||||
| 		case *slack.ConnectionErrorEvent: | 		case *slack.ConnectionErrorEvent: | ||||||
| 			b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj) | 			b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj) | ||||||
| 		case *slack.MemberJoinedChannelEvent: | 		case *slack.MemberJoinedChannelEvent: | ||||||
| 			b.populateUser(ev.User) | 			b.users.populateUser(ev.User) | ||||||
| 		case *slack.LatencyReport: | 		case *slack.HelloEvent, *slack.LatencyReport, *slack.ConnectingEvent: | ||||||
| 			continue | 			continue | ||||||
| 		default: | 		default: | ||||||
| 			b.Log.Debugf("Unhandled incoming event: %T", ev) | 			b.Log.Debugf("Unhandled incoming event: %T", ev) | ||||||
| @@ -126,19 +130,31 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Check for our callback ID | ||||||
|  | 	hasOurCallbackID := false | ||||||
|  | 	if len(ev.Blocks.BlockSet) == 1 { | ||||||
|  | 		block, ok := ev.Blocks.BlockSet[0].(*slack.SectionBlock) | ||||||
|  | 		hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Skip any messages that we made ourselves or from 'slackbot' (see #527). | 	// Skip any messages that we made ourselves or from 'slackbot' (see #527). | ||||||
| 	if ev.Username == sSlackBotUser || | 	if ev.Username == sSlackBotUser || | ||||||
| 		(b.rtm != nil && ev.Username == b.si.User.Name) || | 		(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID { | ||||||
| 		(len(ev.Attachments) > 0 && ev.Attachments[0].CallbackID == "matterbridge_"+b.uuid) { |  | ||||||
| 		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 { | ||||||
| @@ -210,7 +226,7 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) | |||||||
| 		rmsg.Username = sSystemUser | 		rmsg.Username = sSystemUser | ||||||
| 		rmsg.Event = config.EventJoinLeave | 		rmsg.Event = config.EventJoinLeave | ||||||
| 	case sChannelTopic, sChannelPurpose: | 	case sChannelTopic, sChannelPurpose: | ||||||
| 		b.populateChannels(false) | 		b.channels.populateChannels(false) | ||||||
| 		rmsg.Event = config.EventTopicChange | 		rmsg.Event = config.EventTopicChange | ||||||
| 	case sMessageChanged: | 	case sMessageChanged: | ||||||
| 		rmsg.Text = ev.SubMessage.Text | 		rmsg.Text = ev.SubMessage.Text | ||||||
| @@ -266,7 +282,10 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) { | func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) { | ||||||
| 	channelInfo, err := b.getChannelByID(ev.Channel) | 	if ev.User == b.si.User.ID { | ||||||
|  | 		return nil, ErrEventIgnored | ||||||
|  | 	} | ||||||
|  | 	channelInfo, err := b.channels.getChannelByID(ev.Channel) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -316,36 +335,7 @@ func (b *Bslack) handleGetChannelMembers(rmsg *config.Message) bool { | |||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cMembers := config.ChannelMembers{} | 	cMembers := b.channels.getChannelMembers(b.users) | ||||||
|  |  | ||||||
| 	b.channelMembersMutex.RLock() |  | ||||||
|  |  | ||||||
| 	for channelID, members := range b.channelMembers { |  | ||||||
| 		for _, member := range members { |  | ||||||
| 			channelName := "" |  | ||||||
| 			userName := "" |  | ||||||
| 			userNick := "" |  | ||||||
| 			user := b.getUser(member) |  | ||||||
| 			if user != nil { |  | ||||||
| 				userName = user.Name |  | ||||||
| 				userNick = user.Profile.DisplayName |  | ||||||
| 			} |  | ||||||
| 			channel, _ := b.getChannelByID(channelID) |  | ||||||
| 			if channel != nil { |  | ||||||
| 				channelName = channel.Name |  | ||||||
| 			} |  | ||||||
| 			cMember := config.ChannelMember{ |  | ||||||
| 				Username:    userName, |  | ||||||
| 				Nick:        userNick, |  | ||||||
| 				UserID:      member, |  | ||||||
| 				ChannelID:   channelID, |  | ||||||
| 				ChannelName: channelName, |  | ||||||
| 			} |  | ||||||
| 			cMembers = append(cMembers, cMember) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	b.channelMembersMutex.RUnlock() |  | ||||||
|  |  | ||||||
| 	extra := make(map[string][]interface{}) | 	extra := make(map[string][]interface{}) | ||||||
| 	extra[config.EventGetChannelMembers] = append(extra[config.EventGetChannelMembers], cMembers) | 	extra[config.EventGetChannelMembers] = append(extra[config.EventGetChannelMembers], cMembers) | ||||||
|   | |||||||
| @@ -1,233 +1,21 @@ | |||||||
| package bslack | package bslack | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
| 	"github.com/nlopes/slack" | 	"github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/slack-go/slack" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func (b *Bslack) getUser(id string) *slack.User { |  | ||||||
| 	b.usersMutex.RLock() |  | ||||||
| 	user, ok := b.users[id] |  | ||||||
| 	b.usersMutex.RUnlock() |  | ||||||
| 	if ok { |  | ||||||
| 		return user |  | ||||||
| 	} |  | ||||||
| 	b.populateUser(id) |  | ||||||
| 	b.usersMutex.RLock() |  | ||||||
| 	defer b.usersMutex.RUnlock() |  | ||||||
|  |  | ||||||
| 	return b.users[id] |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) getUsername(id string) string { |  | ||||||
| 	if user := b.getUser(id); user != nil { |  | ||||||
| 		if user.Profile.DisplayName != "" { |  | ||||||
| 			return user.Profile.DisplayName |  | ||||||
| 		} |  | ||||||
| 		return user.Name |  | ||||||
| 	} |  | ||||||
| 	b.Log.Warnf("Could not find user with ID '%s'", id) |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) getAvatar(id string) string { |  | ||||||
| 	if user := b.getUser(id); user != nil { |  | ||||||
| 		return user.Profile.Image48 |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) getChannel(channel string) (*slack.Channel, error) { |  | ||||||
| 	if strings.HasPrefix(channel, "ID:") { |  | ||||||
| 		return b.getChannelByID(strings.TrimPrefix(channel, "ID:")) |  | ||||||
| 	} |  | ||||||
| 	return b.getChannelByName(channel) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) { |  | ||||||
| 	return b.getChannelBy(name, b.channelsByName) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) { |  | ||||||
| 	return b.getChannelBy(ID, b.channelsByID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) getChannelBy(lookupKey string, lookupMap map[string]*slack.Channel) (*slack.Channel, error) { |  | ||||||
| 	b.channelsMutex.RLock() |  | ||||||
| 	defer b.channelsMutex.RUnlock() |  | ||||||
|  |  | ||||||
| 	if channel, ok := lookupMap[lookupKey]; ok { |  | ||||||
| 		return channel, nil |  | ||||||
| 	} |  | ||||||
| 	return nil, fmt.Errorf("%s: channel %s not found", b.Account, lookupKey) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const minimumRefreshInterval = 10 * time.Second |  | ||||||
|  |  | ||||||
| func (b *Bslack) populateUser(userID string) { |  | ||||||
| 	b.usersMutex.RLock() |  | ||||||
| 	_, exists := b.users[userID] |  | ||||||
| 	b.usersMutex.RUnlock() |  | ||||||
| 	if exists { |  | ||||||
| 		// already in cache |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	user, err := b.sc.GetUserInfo(userID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		b.Log.Debugf("GetUserInfo failed for %v: %v", userID, err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	b.usersMutex.Lock() |  | ||||||
| 	b.users[userID] = user |  | ||||||
| 	b.usersMutex.Unlock() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) populateUsers(wait bool) { |  | ||||||
| 	b.refreshMutex.Lock() |  | ||||||
| 	if !wait && (time.Now().Before(b.earliestUserRefresh) || b.refreshInProgress) { |  | ||||||
| 		b.Log.Debugf("Not refreshing user list as it was done less than %v ago.", |  | ||||||
| 			minimumRefreshInterval) |  | ||||||
| 		b.refreshMutex.Unlock() |  | ||||||
|  |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for b.refreshInProgress { |  | ||||||
| 		b.refreshMutex.Unlock() |  | ||||||
| 		time.Sleep(time.Second) |  | ||||||
| 		b.refreshMutex.Lock() |  | ||||||
| 	} |  | ||||||
| 	b.refreshInProgress = true |  | ||||||
| 	b.refreshMutex.Unlock() |  | ||||||
|  |  | ||||||
| 	newUsers := map[string]*slack.User{} |  | ||||||
| 	pagination := b.sc.GetUsersPaginated(slack.GetUsersOptionLimit(200)) |  | ||||||
| 	count := 0 |  | ||||||
| 	for { |  | ||||||
| 		var err error |  | ||||||
| 		pagination, err = pagination.Next(context.Background()) |  | ||||||
| 		time.Sleep(time.Second) |  | ||||||
| 		if err != nil { |  | ||||||
| 			if pagination.Done(err) { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if err = b.handleRateLimit(err); err != nil { |  | ||||||
| 				b.Log.Errorf("Could not retrieve users: %#v", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for i := range pagination.Users { |  | ||||||
| 			newUsers[pagination.Users[i].ID] = &pagination.Users[i] |  | ||||||
| 		} |  | ||||||
| 		b.Log.Debugf("getting %d users", len(pagination.Users)) |  | ||||||
| 		count++ |  | ||||||
| 		// more > 2000 users, slack will complain and ratelimit. break |  | ||||||
| 		if count > 10 { |  | ||||||
| 			b.Log.Info("Large slack detected > 2000 users, skipping loading complete userlist.") |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	b.usersMutex.Lock() |  | ||||||
| 	defer b.usersMutex.Unlock() |  | ||||||
| 	b.users = newUsers |  | ||||||
|  |  | ||||||
| 	b.refreshMutex.Lock() |  | ||||||
| 	defer b.refreshMutex.Unlock() |  | ||||||
| 	b.earliestUserRefresh = time.Now().Add(minimumRefreshInterval) |  | ||||||
| 	b.refreshInProgress = false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *Bslack) populateChannels(wait bool) { |  | ||||||
| 	b.refreshMutex.Lock() |  | ||||||
| 	if !wait && (time.Now().Before(b.earliestChannelRefresh) || b.refreshInProgress) { |  | ||||||
| 		b.Log.Debugf("Not refreshing channel list as it was done less than %v seconds ago.", |  | ||||||
| 			minimumRefreshInterval) |  | ||||||
| 		b.refreshMutex.Unlock() |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for b.refreshInProgress { |  | ||||||
| 		b.refreshMutex.Unlock() |  | ||||||
| 		time.Sleep(time.Second) |  | ||||||
| 		b.refreshMutex.Lock() |  | ||||||
| 	} |  | ||||||
| 	b.refreshInProgress = true |  | ||||||
| 	b.refreshMutex.Unlock() |  | ||||||
|  |  | ||||||
| 	newChannelsByID := map[string]*slack.Channel{} |  | ||||||
| 	newChannelsByName := map[string]*slack.Channel{} |  | ||||||
| 	newChannelMembers := make(map[string][]string) |  | ||||||
|  |  | ||||||
| 	// We only retrieve public and private channels, not IMs |  | ||||||
| 	// and MPIMs as those do not have a channel name. |  | ||||||
| 	queryParams := &slack.GetConversationsParameters{ |  | ||||||
| 		ExcludeArchived: "true", |  | ||||||
| 		Types:           []string{"public_channel,private_channel"}, |  | ||||||
| 	} |  | ||||||
| 	for { |  | ||||||
| 		channels, nextCursor, err := b.sc.GetConversations(queryParams) |  | ||||||
| 		if err != nil { |  | ||||||
| 			if err = b.handleRateLimit(err); err != nil { |  | ||||||
| 				b.Log.Errorf("Could not retrieve channels: %#v", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for i := range channels { |  | ||||||
| 			newChannelsByID[channels[i].ID] = &channels[i] |  | ||||||
| 			newChannelsByName[channels[i].Name] = &channels[i] |  | ||||||
| 			// also find all the members in every channel |  | ||||||
| 			// comment for now, issues on big slacks |  | ||||||
| 			/* |  | ||||||
| 				members, err := b.getUsersInConversation(channels[i].ID) |  | ||||||
| 				if err != nil { |  | ||||||
| 					if err = b.handleRateLimit(err); err != nil { |  | ||||||
| 						b.Log.Errorf("Could not retrieve channel members: %#v", err) |  | ||||||
| 						return |  | ||||||
| 					} |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 				newChannelMembers[channels[i].ID] = members |  | ||||||
| 			*/ |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if nextCursor == "" { |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		queryParams.Cursor = nextCursor |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	b.channelsMutex.Lock() |  | ||||||
| 	defer b.channelsMutex.Unlock() |  | ||||||
| 	b.channelsByID = newChannelsByID |  | ||||||
| 	b.channelsByName = newChannelsByName |  | ||||||
|  |  | ||||||
| 	b.channelMembersMutex.Lock() |  | ||||||
| 	defer b.channelMembersMutex.Unlock() |  | ||||||
| 	b.channelMembers = newChannelMembers |  | ||||||
|  |  | ||||||
| 	b.refreshMutex.Lock() |  | ||||||
| 	defer b.refreshMutex.Unlock() |  | ||||||
| 	b.earliestChannelRefresh = time.Now().Add(minimumRefreshInterval) |  | ||||||
| 	b.refreshInProgress = false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // populateReceivedMessage shapes the initial Matterbridge message that we will forward to the | // populateReceivedMessage shapes the initial Matterbridge message that we will forward to the | ||||||
| // router before we apply message-dependent modifications. | // router before we apply message-dependent modifications. | ||||||
| func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Message, error) { | func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Message, error) { | ||||||
| 	// Use our own func because rtm.GetChannelInfo doesn't work for private channels. | 	// Use our own func because rtm.GetChannelInfo doesn't work for private channels. | ||||||
| 	channel, err := b.getChannelByID(ev.Channel) | 	channel, err := b.channels.getChannelByID(ev.Channel) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -289,7 +77,7 @@ func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *confi | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	user := b.getUser(userID) | 	user := b.users.getUser(userID) | ||||||
| 	if user == nil { | 	if user == nil { | ||||||
| 		return fmt.Errorf("could not find information for user with id %s", ev.User) | 		return fmt.Errorf("could not find information for user with id %s", ev.User) | ||||||
| 	} | 	} | ||||||
| @@ -315,7 +103,7 @@ func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config | |||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err = b.handleRateLimit(err); err != nil { | 		if err = handleRateLimit(b.Log, err); err != nil { | ||||||
| 			b.Log.Errorf("Could not retrieve bot information: %#v", err) | 			b.Log.Errorf("Could not retrieve bot information: %#v", err) | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -360,7 +148,7 @@ func (b *Bslack) extractTopicOrPurpose(text string) (string, string) { | |||||||
| func (b *Bslack) replaceMention(text string) string { | func (b *Bslack) replaceMention(text string) string { | ||||||
| 	replaceFunc := func(match string) string { | 	replaceFunc := func(match string) string { | ||||||
| 		userID := strings.Trim(match, "@<>") | 		userID := strings.Trim(match, "@<>") | ||||||
| 		if username := b.getUsername(userID); userID != "" { | 		if username := b.users.getUsername(userID); userID != "" { | ||||||
| 			return "@" + username | 			return "@" + username | ||||||
| 		} | 		} | ||||||
| 		return match | 		return match | ||||||
| @@ -400,18 +188,38 @@ func (b *Bslack) replaceURL(text string) string { | |||||||
| 	return text | 	return text | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bslack) replaceCodeFence(text string) string { | func (b *Bslack) replaceb0rkedMarkDown(text string) string { | ||||||
| 	return codeFenceRE.ReplaceAllString(text, "```") | 	// taken from https://github.com/mattermost/mattermost-server/blob/master/app/slackimport.go | ||||||
|  | 	// | ||||||
|  | 	regexReplaceAllString := []struct { | ||||||
|  | 		regex *regexp.Regexp | ||||||
|  | 		rpl   string | ||||||
|  | 	}{ | ||||||
|  | 		// bold | ||||||
|  | 		{ | ||||||
|  | 			regexp.MustCompile(`(^|[\s.;,])\*(\S[^*\n]+)\*`), | ||||||
|  | 			"$1**$2**", | ||||||
|  | 		}, | ||||||
|  | 		// strikethrough | ||||||
|  | 		{ | ||||||
|  | 			regexp.MustCompile(`(^|[\s.;,])\~(\S[^~\n]+)\~`), | ||||||
|  | 			"$1~~$2~~", | ||||||
|  | 		}, | ||||||
|  | 		// single paragraph blockquote | ||||||
|  | 		// Slack converts > character to > | ||||||
|  | 		{ | ||||||
|  | 			regexp.MustCompile(`(?sm)^>`), | ||||||
|  | 			">", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for _, rule := range regexReplaceAllString { | ||||||
|  | 		text = rule.regex.ReplaceAllString(text, rule.rpl) | ||||||
|  | 	} | ||||||
|  | 	return text | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bslack) handleRateLimit(err error) error { | func (b *Bslack) replaceCodeFence(text string) string { | ||||||
| 	rateLimit, ok := err.(*slack.RateLimitedError) | 	return codeFenceRE.ReplaceAllString(text, "```") | ||||||
| 	if !ok { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	b.Log.Infof("Rate-limited by Slack. Sleeping for %v", rateLimit.RetryAfter) |  | ||||||
| 	time.Sleep(rateLimit.RetryAfter) |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // getUsersInConversation returns an array of userIDs that are members of channelID | // getUsersInConversation returns an array of userIDs that are members of channelID | ||||||
| @@ -424,7 +232,7 @@ func (b *Bslack) getUsersInConversation(channelID string) ([]string, error) { | |||||||
|  |  | ||||||
| 		members, nextCursor, err := b.sc.GetUsersInConversation(queryParams) | 		members, nextCursor, err := b.sc.GetUsersInConversation(queryParams) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if err = b.handleRateLimit(err); err != nil { | 			if err = handleRateLimit(b.Log, err); err != nil { | ||||||
| 				return channelMembers, fmt.Errorf("Could not retrieve users in channels: %#v", err) | 				return channelMembers, fmt.Errorf("Could not retrieve users in channels: %#v", err) | ||||||
| 			} | 			} | ||||||
| 			continue | 			continue | ||||||
| @@ -439,3 +247,13 @@ func (b *Bslack) getUsersInConversation(channelID string) ([]string, error) { | |||||||
| 	} | 	} | ||||||
| 	return channelMembers, nil | 	return channelMembers, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func handleRateLimit(log *logrus.Entry, err error) error { | ||||||
|  | 	rateLimit, ok := err.(*slack.RateLimitedError) | ||||||
|  | 	if !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	log.Infof("Rate-limited by Slack. Sleeping for %v", rateLimit.RetryAfter) | ||||||
|  | 	time.Sleep(rateLimit.RetryAfter) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge" | 	"github.com/42wim/matterbridge/bridge" | ||||||
| 	"github.com/42wim/matterbridge/matterhook" | 	"github.com/42wim/matterbridge/matterhook" | ||||||
| 	"github.com/nlopes/slack" | 	"github.com/slack-go/slack" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type BLegacy struct { | type BLegacy struct { | ||||||
| @@ -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 { | ||||||
| @@ -55,14 +57,18 @@ func (b *BLegacy) Connect() error { | |||||||
| 		}) | 		}) | ||||||
| 		if b.GetString(tokenConfig) != "" { | 		if b.GetString(tokenConfig) != "" { | ||||||
| 			b.Log.Info("Connecting using token (receiving)") | 			b.Log.Info("Connecting using token (receiving)") | ||||||
| 			b.sc = slack.New(b.GetString(tokenConfig)) | 			b.sc = slack.New(b.GetString(tokenConfig), slack.OptionDebug(b.GetBool("debug"))) | ||||||
|  | 			b.channels = newChannelManager(b.Log, b.sc) | ||||||
|  | 			b.users = newUserManager(b.Log, b.sc) | ||||||
| 			b.rtm = b.sc.NewRTM() | 			b.rtm = b.sc.NewRTM() | ||||||
| 			go b.rtm.ManageConnection() | 			go b.rtm.ManageConnection() | ||||||
| 			go b.handleSlack() | 			go b.handleSlack() | ||||||
| 		} | 		} | ||||||
| 	} else if b.GetString(tokenConfig) != "" { | 	} else if b.GetString(tokenConfig) != "" { | ||||||
| 		b.Log.Info("Connecting using token (sending and receiving)") | 		b.Log.Info("Connecting using token (sending and receiving)") | ||||||
| 		b.sc = slack.New(b.GetString(tokenConfig)) | 		b.sc = slack.New(b.GetString(tokenConfig), slack.OptionDebug(b.GetBool("debug"))) | ||||||
|  | 		b.channels = newChannelManager(b.Log, b.sc) | ||||||
|  | 		b.users = newUserManager(b.Log, b.sc) | ||||||
| 		b.rtm = b.sc.NewRTM() | 		b.rtm = b.sc.NewRTM() | ||||||
| 		go b.rtm.ManageConnection() | 		go b.rtm.ManageConnection() | ||||||
| 		go b.handleSlack() | 		go b.handleSlack() | ||||||
|   | |||||||
| @@ -12,9 +12,9 @@ import ( | |||||||
| 	"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/42wim/matterbridge/matterhook" | 	"github.com/42wim/matterbridge/matterhook" | ||||||
| 	"github.com/hashicorp/golang-lru" | 	lru "github.com/hashicorp/golang-lru" | ||||||
| 	"github.com/nlopes/slack" |  | ||||||
| 	"github.com/rs/xid" | 	"github.com/rs/xid" | ||||||
|  | 	"github.com/slack-go/slack" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Bslack struct { | type Bslack struct { | ||||||
| @@ -30,23 +30,13 @@ type Bslack struct { | |||||||
| 	uuid         string | 	uuid         string | ||||||
| 	useChannelID bool | 	useChannelID bool | ||||||
|  |  | ||||||
| 	users      map[string]*slack.User | 	channels *channels | ||||||
| 	usersMutex sync.RWMutex | 	users    *users | ||||||
|  | 	legacy   bool | ||||||
| 	channelsByID   map[string]*slack.Channel |  | ||||||
| 	channelsByName map[string]*slack.Channel |  | ||||||
| 	channelsMutex  sync.RWMutex |  | ||||||
|  |  | ||||||
| 	channelMembers      map[string][]string |  | ||||||
| 	channelMembersMutex sync.RWMutex |  | ||||||
|  |  | ||||||
| 	refreshInProgress      bool |  | ||||||
| 	earliestChannelRefresh time.Time |  | ||||||
| 	earliestUserRefresh    time.Time |  | ||||||
| 	refreshMutex           sync.Mutex |  | ||||||
| } | } | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
|  | 	sHello           = "hello" | ||||||
| 	sChannelJoin     = "channel_join" | 	sChannelJoin     = "channel_join" | ||||||
| 	sChannelLeave    = "channel_leave" | 	sChannelLeave    = "channel_leave" | ||||||
| 	sChannelJoined   = "channel_joined" | 	sChannelJoined   = "channel_joined" | ||||||
| @@ -94,14 +84,9 @@ func newBridge(cfg *bridge.Config) *Bslack { | |||||||
| 		cfg.Log.Fatalf("Could not create LRU cache for Slack bridge: %v", err) | 		cfg.Log.Fatalf("Could not create LRU cache for Slack bridge: %v", err) | ||||||
| 	} | 	} | ||||||
| 	b := &Bslack{ | 	b := &Bslack{ | ||||||
| 		Config:                 cfg, | 		Config: cfg, | ||||||
| 		uuid:                   xid.New().String(), | 		uuid:   xid.New().String(), | ||||||
| 		cache:                  newCache, | 		cache:  newCache, | ||||||
| 		users:                  map[string]*slack.User{}, |  | ||||||
| 		channelsByID:           map[string]*slack.Channel{}, |  | ||||||
| 		channelsByName:         map[string]*slack.Channel{}, |  | ||||||
| 		earliestChannelRefresh: time.Now(), |  | ||||||
| 		earliestUserRefresh:    time.Now(), |  | ||||||
| 	} | 	} | ||||||
| 	return b | 	return b | ||||||
| } | } | ||||||
| @@ -121,7 +106,12 @@ func (b *Bslack) Connect() error { | |||||||
| 	// If we have a token we use the Slack websocket-based RTM for both sending and receiving. | 	// If we have a token we use the Slack websocket-based RTM for both sending and receiving. | ||||||
| 	if token := b.GetString(tokenConfig); token != "" { | 	if token := b.GetString(tokenConfig); token != "" { | ||||||
| 		b.Log.Info("Connecting using token") | 		b.Log.Info("Connecting using token") | ||||||
|  |  | ||||||
| 		b.sc = slack.New(token, slack.OptionDebug(b.GetBool("Debug"))) | 		b.sc = slack.New(token, slack.OptionDebug(b.GetBool("Debug"))) | ||||||
|  |  | ||||||
|  | 		b.channels = newChannelManager(b.Log, b.sc) | ||||||
|  | 		b.users = newUserManager(b.Log, b.sc) | ||||||
|  |  | ||||||
| 		b.rtm = b.sc.NewRTM() | 		b.rtm = b.sc.NewRTM() | ||||||
| 		go b.rtm.ManageConnection() | 		go b.rtm.ManageConnection() | ||||||
| 		go b.handleSlack() | 		go b.handleSlack() | ||||||
| @@ -163,9 +153,21 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.populateChannels(false) | 	// 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 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	channelInfo, err := b.getChannel(channel.Name) | 	b.channels.populateChannels(false) | ||||||
|  |  | ||||||
|  | 	channelInfo, err := b.channels.getChannel(channel.Name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("could not join channel: %#v", err) | 		return fmt.Errorf("could not join channel: %#v", err) | ||||||
| 	} | 	} | ||||||
| @@ -175,7 +177,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 | ||||||
| @@ -275,7 +278,7 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) { | |||||||
| 		return "", nil | 		return "", nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	channelInfo, err := b.getChannel(msg.Channel) | 	channelInfo, err := b.channels.getChannel(msg.Channel) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", fmt.Errorf("could not send message: %v", err) | 		return "", fmt.Errorf("could not send message: %v", err) | ||||||
| 	} | 	} | ||||||
| @@ -351,7 +354,7 @@ func (b *Bslack) updateTopicOrPurpose(msg *config.Message, channelInfo *slack.Ch | |||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| 		if err = b.handleRateLimit(err); err != nil { | 		if err = handleRateLimit(b.Log, err); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -392,7 +395,7 @@ func (b *Bslack) deleteMessage(msg *config.Message, channelInfo *slack.Channel) | |||||||
| 			return true, nil | 			return true, nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err = b.handleRateLimit(err); err != nil { | 		if err = handleRateLimit(b.Log, err); err != nil { | ||||||
| 			b.Log.Errorf("Failed to delete user message from Slack: %#v", err) | 			b.Log.Errorf("Failed to delete user message from Slack: %#v", err) | ||||||
| 			return true, err | 			return true, err | ||||||
| 		} | 		} | ||||||
| @@ -405,13 +408,12 @@ func (b *Bslack) editMessage(msg *config.Message, channelInfo *slack.Channel) (b | |||||||
| 	} | 	} | ||||||
| 	messageOptions := b.prepareMessageOptions(msg) | 	messageOptions := b.prepareMessageOptions(msg) | ||||||
| 	for { | 	for { | ||||||
| 		messageOptions = append(messageOptions, slack.MsgOptionText(msg.Text, false)) |  | ||||||
| 		_, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...) | 		_, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			return true, nil | 			return true, nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err = b.handleRateLimit(err); err != nil { | 		if err = handleRateLimit(b.Log, err); err != nil { | ||||||
| 			b.Log.Errorf("Failed to edit user message on Slack: %#v", err) | 			b.Log.Errorf("Failed to edit user message on Slack: %#v", err) | ||||||
| 			return true, err | 			return true, err | ||||||
| 		} | 		} | ||||||
| @@ -424,14 +426,13 @@ func (b *Bslack) postMessage(msg *config.Message, channelInfo *slack.Channel) (s | |||||||
| 		return "", nil | 		return "", nil | ||||||
| 	} | 	} | ||||||
| 	messageOptions := b.prepareMessageOptions(msg) | 	messageOptions := b.prepareMessageOptions(msg) | ||||||
| 	messageOptions = append(messageOptions, slack.MsgOptionText(msg.Text, false)) |  | ||||||
| 	for { | 	for { | ||||||
| 		_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...) | 		_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			return id, nil | 			return id, nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err = b.handleRateLimit(err); err != nil { | 		if err = handleRateLimit(b.Log, err); err != nil { | ||||||
| 			b.Log.Errorf("Failed to sent user message to Slack: %#v", err) | 			b.Log.Errorf("Failed to sent user message to Slack: %#v", err) | ||||||
| 			return "", err | 			return "", err | ||||||
| 		} | 		} | ||||||
| @@ -490,8 +491,6 @@ func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var attachments []slack.Attachment | 	var attachments []slack.Attachment | ||||||
| 	// add a callback ID so we can see we created it |  | ||||||
| 	attachments = append(attachments, slack.Attachment{CallbackID: "matterbridge_" + b.uuid}) |  | ||||||
| 	// add file attachments | 	// add file attachments | ||||||
| 	attachments = append(attachments, b.createAttach(msg.Extra)...) | 	attachments = append(attachments, b.createAttach(msg.Extra)...) | ||||||
| 	// add slack attachments (from another slack bridge) | 	// add slack attachments (from another slack bridge) | ||||||
| @@ -502,6 +501,19 @@ func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var opts []slack.MsgOption | 	var opts []slack.MsgOption | ||||||
|  | 	opts = append(opts, | ||||||
|  | 		// provide regular text field (fallback used in Slack notifications, etc.) | ||||||
|  | 		slack.MsgOptionText(msg.Text, false), | ||||||
|  |  | ||||||
|  | 		// add a callback ID so we can see we created it | ||||||
|  | 		slack.MsgOptionBlocks(slack.NewSectionBlock( | ||||||
|  | 			slack.NewTextBlockObject(slack.MarkdownType, msg.Text, false, false), | ||||||
|  | 			nil, nil, | ||||||
|  | 			slack.SectionBlockOptionBlockID("matterbridge_"+b.uuid), | ||||||
|  | 		)), | ||||||
|  |  | ||||||
|  | 		slack.MsgOptionEnableLinkUnfurl(), | ||||||
|  | 	) | ||||||
| 	opts = append(opts, slack.MsgOptionAttachments(attachments...)) | 	opts = append(opts, slack.MsgOptionAttachments(attachments...)) | ||||||
| 	opts = append(opts, slack.MsgOptionPostMessageParameters(params)) | 	opts = append(opts, slack.MsgOptionPostMessageParameters(params)) | ||||||
| 	return opts | 	return opts | ||||||
|   | |||||||
							
								
								
									
										336
									
								
								bridge/slack/users_channels.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								bridge/slack/users_channels.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | |||||||
|  | package bslack | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/slack-go/slack" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const minimumRefreshInterval = 10 * time.Second | ||||||
|  |  | ||||||
|  | type users struct { | ||||||
|  | 	log *logrus.Entry | ||||||
|  | 	sc  *slack.Client | ||||||
|  |  | ||||||
|  | 	users           map[string]*slack.User | ||||||
|  | 	usersMutex      sync.RWMutex | ||||||
|  | 	usersSyncPoints map[string]chan struct{} | ||||||
|  |  | ||||||
|  | 	refreshInProgress bool | ||||||
|  | 	earliestRefresh   time.Time | ||||||
|  | 	refreshMutex      sync.Mutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newUserManager(log *logrus.Entry, sc *slack.Client) *users { | ||||||
|  | 	return &users{ | ||||||
|  | 		log:             log, | ||||||
|  | 		sc:              sc, | ||||||
|  | 		users:           make(map[string]*slack.User), | ||||||
|  | 		usersSyncPoints: make(map[string]chan struct{}), | ||||||
|  | 		earliestRefresh: time.Now(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *users) getUser(id string) *slack.User { | ||||||
|  | 	b.usersMutex.RLock() | ||||||
|  | 	user, ok := b.users[id] | ||||||
|  | 	b.usersMutex.RUnlock() | ||||||
|  | 	if ok { | ||||||
|  | 		return user | ||||||
|  | 	} | ||||||
|  | 	b.populateUser(id) | ||||||
|  | 	b.usersMutex.RLock() | ||||||
|  | 	defer b.usersMutex.RUnlock() | ||||||
|  |  | ||||||
|  | 	return b.users[id] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *users) getUsername(id string) string { | ||||||
|  | 	if user := b.getUser(id); user != nil { | ||||||
|  | 		if user.Profile.DisplayName != "" { | ||||||
|  | 			return user.Profile.DisplayName | ||||||
|  | 		} | ||||||
|  | 		return user.Name | ||||||
|  | 	} | ||||||
|  | 	b.log.Warnf("Could not find user with ID '%s'", id) | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *users) getAvatar(id string) string { | ||||||
|  | 	if user := b.getUser(id); user != nil { | ||||||
|  | 		return user.Profile.Image48 | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *users) populateUser(userID string) { | ||||||
|  | 	for { | ||||||
|  | 		b.usersMutex.Lock() | ||||||
|  | 		_, exists := b.users[userID] | ||||||
|  | 		if exists { | ||||||
|  | 			// already in cache | ||||||
|  | 			b.usersMutex.Unlock() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if syncPoint, ok := b.usersSyncPoints[userID]; ok { | ||||||
|  | 			// Another goroutine is already populating this user for us so wait on it to finish. | ||||||
|  | 			b.usersMutex.Unlock() | ||||||
|  | 			<-syncPoint | ||||||
|  | 			// We do not return and iterate again to check that the entry does indeed exist | ||||||
|  | 			// in case the previous query failed for some reason. | ||||||
|  | 		} else { | ||||||
|  | 			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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Do not hold the lock while fetching information from Slack | ||||||
|  | 	// as this might take an unbounded amount of time. | ||||||
|  | 	b.usersMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	user, err := b.sc.GetUserInfo(userID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.log.Debugf("GetUserInfo failed for %v: %v", userID, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.usersMutex.Lock() | ||||||
|  | 	defer b.usersMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	// Register user information. | ||||||
|  | 	b.users[userID] = user | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *users) populateUsers(wait bool) { | ||||||
|  | 	b.refreshMutex.Lock() | ||||||
|  | 	if !wait && (time.Now().Before(b.earliestRefresh) || b.refreshInProgress) { | ||||||
|  | 		b.log.Debugf("Not refreshing user list as it was done less than %v ago.", minimumRefreshInterval) | ||||||
|  | 		b.refreshMutex.Unlock() | ||||||
|  |  | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	for b.refreshInProgress { | ||||||
|  | 		b.refreshMutex.Unlock() | ||||||
|  | 		time.Sleep(time.Second) | ||||||
|  | 		b.refreshMutex.Lock() | ||||||
|  | 	} | ||||||
|  | 	b.refreshInProgress = true | ||||||
|  | 	b.refreshMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	newUsers := map[string]*slack.User{} | ||||||
|  | 	pagination := b.sc.GetUsersPaginated(slack.GetUsersOptionLimit(200)) | ||||||
|  | 	count := 0 | ||||||
|  | 	for { | ||||||
|  | 		var err error | ||||||
|  | 		pagination, err = pagination.Next(context.Background()) | ||||||
|  | 		time.Sleep(time.Second) | ||||||
|  | 		if err != nil { | ||||||
|  | 			if pagination.Done(err) { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err = handleRateLimit(b.log, err); err != nil { | ||||||
|  | 				b.log.Errorf("Could not retrieve users: %#v", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for i := range pagination.Users { | ||||||
|  | 			newUsers[pagination.Users[i].ID] = &pagination.Users[i] | ||||||
|  | 		} | ||||||
|  | 		b.log.Debugf("getting %d users", len(pagination.Users)) | ||||||
|  | 		count++ | ||||||
|  | 		// more > 2000 users, slack will complain and ratelimit. break | ||||||
|  | 		if count > 10 { | ||||||
|  | 			b.log.Info("Large slack detected > 2000 users, skipping loading complete userlist.") | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.usersMutex.Lock() | ||||||
|  | 	defer b.usersMutex.Unlock() | ||||||
|  | 	b.users = newUsers | ||||||
|  |  | ||||||
|  | 	b.refreshMutex.Lock() | ||||||
|  | 	defer b.refreshMutex.Unlock() | ||||||
|  | 	b.earliestRefresh = time.Now().Add(minimumRefreshInterval) | ||||||
|  | 	b.refreshInProgress = false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type channels struct { | ||||||
|  | 	log *logrus.Entry | ||||||
|  | 	sc  *slack.Client | ||||||
|  |  | ||||||
|  | 	channelsByID   map[string]*slack.Channel | ||||||
|  | 	channelsByName map[string]*slack.Channel | ||||||
|  | 	channelsMutex  sync.RWMutex | ||||||
|  |  | ||||||
|  | 	channelMembers      map[string][]string | ||||||
|  | 	channelMembersMutex sync.RWMutex | ||||||
|  |  | ||||||
|  | 	refreshInProgress bool | ||||||
|  | 	earliestRefresh   time.Time | ||||||
|  | 	refreshMutex      sync.Mutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newChannelManager(log *logrus.Entry, sc *slack.Client) *channels { | ||||||
|  | 	return &channels{ | ||||||
|  | 		log:             log, | ||||||
|  | 		sc:              sc, | ||||||
|  | 		channelsByID:    make(map[string]*slack.Channel), | ||||||
|  | 		channelsByName:  make(map[string]*slack.Channel), | ||||||
|  | 		earliestRefresh: time.Now(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) getChannel(channel string) (*slack.Channel, error) { | ||||||
|  | 	if strings.HasPrefix(channel, "ID:") { | ||||||
|  | 		return b.getChannelByID(strings.TrimPrefix(channel, "ID:")) | ||||||
|  | 	} | ||||||
|  | 	return b.getChannelByName(channel) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) getChannelByName(name string) (*slack.Channel, error) { | ||||||
|  | 	return b.getChannelBy(name, b.channelsByName) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) getChannelByID(id string) (*slack.Channel, error) { | ||||||
|  | 	return b.getChannelBy(id, b.channelsByID) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) getChannelBy(lookupKey string, lookupMap map[string]*slack.Channel) (*slack.Channel, error) { | ||||||
|  | 	b.channelsMutex.RLock() | ||||||
|  | 	defer b.channelsMutex.RUnlock() | ||||||
|  |  | ||||||
|  | 	if channel, ok := lookupMap[lookupKey]; ok { | ||||||
|  | 		return channel, nil | ||||||
|  | 	} | ||||||
|  | 	return nil, fmt.Errorf("channel %s not found", lookupKey) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) getChannelMembers(users *users) config.ChannelMembers { | ||||||
|  | 	b.channelMembersMutex.RLock() | ||||||
|  | 	defer b.channelMembersMutex.RUnlock() | ||||||
|  |  | ||||||
|  | 	membersInfo := config.ChannelMembers{} | ||||||
|  | 	for channelID, members := range b.channelMembers { | ||||||
|  | 		for _, member := range members { | ||||||
|  | 			channelName := "" | ||||||
|  | 			userName := "" | ||||||
|  | 			userNick := "" | ||||||
|  | 			user := users.getUser(member) | ||||||
|  | 			if user != nil { | ||||||
|  | 				userName = user.Name | ||||||
|  | 				userNick = user.Profile.DisplayName | ||||||
|  | 			} | ||||||
|  | 			channel, _ := b.getChannelByID(channelID) | ||||||
|  | 			if channel != nil { | ||||||
|  | 				channelName = channel.Name | ||||||
|  | 			} | ||||||
|  | 			memberInfo := config.ChannelMember{ | ||||||
|  | 				Username:    userName, | ||||||
|  | 				Nick:        userNick, | ||||||
|  | 				UserID:      member, | ||||||
|  | 				ChannelID:   channelID, | ||||||
|  | 				ChannelName: channelName, | ||||||
|  | 			} | ||||||
|  | 			membersInfo = append(membersInfo, memberInfo) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return membersInfo | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) registerChannel(channel slack.Channel) { | ||||||
|  | 	b.channelsMutex.Lock() | ||||||
|  | 	defer b.channelsMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	b.channelsByID[channel.ID] = &channel | ||||||
|  | 	b.channelsByName[channel.Name] = &channel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *channels) populateChannels(wait bool) { | ||||||
|  | 	b.refreshMutex.Lock() | ||||||
|  | 	if !wait && (time.Now().Before(b.earliestRefresh) || b.refreshInProgress) { | ||||||
|  | 		b.log.Debugf("Not refreshing channel list as it was done less than %v seconds ago.", minimumRefreshInterval) | ||||||
|  | 		b.refreshMutex.Unlock() | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	for b.refreshInProgress { | ||||||
|  | 		b.refreshMutex.Unlock() | ||||||
|  | 		time.Sleep(time.Second) | ||||||
|  | 		b.refreshMutex.Lock() | ||||||
|  | 	} | ||||||
|  | 	b.refreshInProgress = true | ||||||
|  | 	b.refreshMutex.Unlock() | ||||||
|  |  | ||||||
|  | 	newChannelsByID := map[string]*slack.Channel{} | ||||||
|  | 	newChannelsByName := map[string]*slack.Channel{} | ||||||
|  | 	newChannelMembers := make(map[string][]string) | ||||||
|  |  | ||||||
|  | 	// We only retrieve public and private channels, not IMs | ||||||
|  | 	// and MPIMs as those do not have a channel name. | ||||||
|  | 	queryParams := &slack.GetConversationsParameters{ | ||||||
|  | 		ExcludeArchived: "true", | ||||||
|  | 		Types:           []string{"public_channel,private_channel"}, | ||||||
|  | 	} | ||||||
|  | 	for { | ||||||
|  | 		channels, nextCursor, err := b.sc.GetConversations(queryParams) | ||||||
|  | 		if err != nil { | ||||||
|  | 			if err = handleRateLimit(b.log, err); err != nil { | ||||||
|  | 				b.log.Errorf("Could not retrieve channels: %#v", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for i := range channels { | ||||||
|  | 			newChannelsByID[channels[i].ID] = &channels[i] | ||||||
|  | 			newChannelsByName[channels[i].Name] = &channels[i] | ||||||
|  | 			// also find all the members in every channel | ||||||
|  | 			// comment for now, issues on big slacks | ||||||
|  | 			/* | ||||||
|  | 				members, err := b.getUsersInConversation(channels[i].ID) | ||||||
|  | 				if err != nil { | ||||||
|  | 					if err = b.handleRateLimit(err); err != nil { | ||||||
|  | 						b.Log.Errorf("Could not retrieve channel members: %#v", err) | ||||||
|  | 						return | ||||||
|  | 					} | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				newChannelMembers[channels[i].ID] = members | ||||||
|  | 			*/ | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if nextCursor == "" { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		queryParams.Cursor = nextCursor | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.channelsMutex.Lock() | ||||||
|  | 	defer b.channelsMutex.Unlock() | ||||||
|  | 	b.channelsByID = newChannelsByID | ||||||
|  | 	b.channelsByName = newChannelsByName | ||||||
|  |  | ||||||
|  | 	b.channelMembersMutex.Lock() | ||||||
|  | 	defer b.channelMembersMutex.Unlock() | ||||||
|  | 	b.channelMembers = newChannelMembers | ||||||
|  |  | ||||||
|  | 	b.refreshMutex.Lock() | ||||||
|  | 	defer b.refreshMutex.Unlock() | ||||||
|  | 	b.earliestRefresh = time.Now().Add(minimumRefreshInterval) | ||||||
|  | 	b.refreshInProgress = false | ||||||
|  | } | ||||||
| @@ -130,6 +130,10 @@ func (b *Bsshchat) handleSSHChat() error { | |||||||
| 			if strings.Contains(b.r.Text(), "Rate limiting is in effect") { | 			if strings.Contains(b.r.Text(), "Rate limiting is in effect") { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  | 			// skip our own messages | ||||||
|  | 			if !strings.HasPrefix(b.r.Text(), "["+b.GetString("Nick")+"] \x1b") { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
| 			res := strings.Split(stripPrompt(b.r.Text()), ":") | 			res := strings.Split(stripPrompt(b.r.Text()), ":") | ||||||
| 			if res[0] == "-> Set theme" { | 			if res[0] == "-> Set theme" { | ||||||
| 				wait = false | 				wait = false | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ func (b *Bsteam) handleEvents() { | |||||||
|  |  | ||||||
| func (b *Bsteam) handleLogOnFailed(e *steam.LogOnFailedEvent, myLoginInfo *steam.LogOnDetails) error { | func (b *Bsteam) handleLogOnFailed(e *steam.LogOnFailedEvent, myLoginInfo *steam.LogOnDetails) error { | ||||||
| 	switch e.Result { | 	switch e.Result { | ||||||
| 	case steamlang.EResult_AccountLogonDeniedNeedTwoFactorCode: | 	case steamlang.EResult_AccountLoginDeniedNeedTwoFactor: | ||||||
| 		b.Log.Info("Steam guard isn't letting me in! Enter 2FA code:") | 		b.Log.Info("Steam guard isn't letting me in! Enter 2FA code:") | ||||||
| 		var code string | 		var code string | ||||||
| 		fmt.Scanf("%s", &code) | 		fmt.Scanf("%s", &code) | ||||||
|   | |||||||
| @@ -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 { | ||||||
| @@ -94,7 +95,7 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// only download avatars if we have a place to upload them (configured mediaserver) | 		// only download avatars if we have a place to upload them (configured mediaserver) | ||||||
| 		if b.General.MediaServerUpload != "" { | 		if b.General.MediaServerUpload != "" || (b.General.MediaServerDownload != "" && b.General.MediaDownloadPath != "") { | ||||||
| 			b.handleDownloadAvatar(message.From.ID, rmsg.Channel) | 			b.handleDownloadAvatar(message.From.ID, rmsg.Channel) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -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) | ||||||
| @@ -144,6 +150,9 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | |||||||
| 		// quote the previous message | 		// quote the previous message | ||||||
| 		b.handleQuoting(&rmsg, message) | 		b.handleQuoting(&rmsg, message) | ||||||
|  |  | ||||||
|  | 		// handle entities (adding URLs) | ||||||
|  | 		b.handleEntities(&rmsg, message) | ||||||
|  |  | ||||||
| 		if rmsg.Text != "" || len(rmsg.Extra) > 0 { | 		if rmsg.Text != "" || len(rmsg.Extra) > 0 { | ||||||
| 			rmsg.Text = helper.RemoveEmptyNewLines(rmsg.Text) | 			rmsg.Text = helper.RemoveEmptyNewLines(rmsg.Text) | ||||||
| 			// channels don't have (always?) user information. see #410 | 			// channels don't have (always?) user information. see #410 | ||||||
| @@ -245,6 +254,15 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	if strings.HasSuffix(name, ".webp") && b.GetBool("MediaConvertWebPToPNG") { | ||||||
|  | 		b.Log.Debugf("WebP to PNG conversion enabled, converting %s", name) | ||||||
|  | 		err := helper.ConvertWebPToPNG(data) | ||||||
|  | 		if err != nil { | ||||||
|  | 			b.Log.Errorf("conversion failed: %s", err) | ||||||
|  | 		} else { | ||||||
|  | 			name = strings.Replace(name, ".webp", ".png", 1) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, "", data, b.General) | 	helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, "", data, b.General) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -339,8 +357,40 @@ func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string | |||||||
| 	if format == "" { | 	if format == "" { | ||||||
| 		format = "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})" | 		format = "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})" | ||||||
| 	} | 	} | ||||||
|  | 	quoteMessagelength := len(quoteMessage) | ||||||
|  | 	if b.GetInt("QuoteLengthLimit") != 0 && quoteMessagelength >= b.GetInt("QuoteLengthLimit") { | ||||||
|  | 		runes := []rune(quoteMessage) | ||||||
|  | 		quoteMessage = string(runes[0:b.GetInt("QuoteLengthLimit")]) | ||||||
|  | 		if quoteMessagelength > b.GetInt("QuoteLengthLimit") { | ||||||
|  | 			quoteMessage += "..." | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	format = strings.Replace(format, "{MESSAGE}", message, -1) | 	format = strings.Replace(format, "{MESSAGE}", message, -1) | ||||||
| 	format = strings.Replace(format, "{QUOTENICK}", quoteNick, -1) | 	format = strings.Replace(format, "{QUOTENICK}", quoteNick, -1) | ||||||
| 	format = strings.Replace(format, "{QUOTEMESSAGE}", quoteMessage, -1) | 	format = strings.Replace(format, "{QUOTEMESSAGE}", quoteMessage, -1) | ||||||
| 	return format | 	return format | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // handleEntities handles messageEntities | ||||||
|  | func (b *Btelegram) handleEntities(rmsg *config.Message, message *tgbotapi.Message) { | ||||||
|  | 	if message.Entities == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// for now only do URL replacements | ||||||
|  | 	for _, e := range *message.Entities { | ||||||
|  | 		if e.Type == "text_link" { | ||||||
|  | 			url, err := e.ParseURL() | ||||||
|  | 			if err != nil { | ||||||
|  | 				b.Log.Errorf("entity text_link url parse failed: %s", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			utfEncodedString := utf16.Encode([]rune(rmsg.Text)) | ||||||
|  | 			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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ package btelegram | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"html" | 	"html" | ||||||
| 	"io" |  | ||||||
|  |  | ||||||
| 	"github.com/russross/blackfriday" | 	"github.com/russross/blackfriday" | ||||||
| ) | ) | ||||||
| @@ -33,7 +32,7 @@ func (options *customHTML) Header(out *bytes.Buffer, text func() bool, level int | |||||||
| 	options.Paragraph(out, text) | 	options.Paragraph(out, text) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (options *customHTML) HRule(out io.ByteWriter) { | func (options *customHTML) HRule(out *bytes.Buffer) { | ||||||
| 	out.WriteByte('\n') //nolint:errcheck | 	out.WriteByte('\n') //nolint:errcheck | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -54,16 +53,13 @@ func (options *customHTML) ListItem(out *bytes.Buffer, text []byte, flags int) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func makeHTML(input string) string { | func makeHTML(input string) string { | ||||||
| 	extensions := blackfriday.NoIntraEmphasis | | 	return string(blackfriday.Markdown([]byte(input), | ||||||
| 		blackfriday.FencedCode | | 		&customHTML{blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML|blackfriday.HTML_SKIP_IMAGES, "", "")}, | ||||||
| 		blackfriday.Autolink | | 		blackfriday.EXTENSION_NO_INTRA_EMPHASIS| | ||||||
| 		blackfriday.SpaceHeadings | | 			blackfriday.EXTENSION_FENCED_CODE| | ||||||
| 		blackfriday.HeadingIDs | | 			blackfriday.EXTENSION_AUTOLINK| | ||||||
| 		blackfriday.BackslashLineBreak | | 			blackfriday.EXTENSION_SPACE_HEADERS| | ||||||
| 		blackfriday.DefinitionLists | 			blackfriday.EXTENSION_HEADER_IDS| | ||||||
|  | 			blackfriday.EXTENSION_BACKSLASH_LINE_BREAK| | ||||||
| 	renderer := &customHTML{blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ | 			blackfriday.EXTENSION_DEFINITION_LISTS)) | ||||||
| 		Flags: blackfriday.UseXHTML | blackfriday.SkipImages, |  | ||||||
| 	})} |  | ||||||
| 	return string(blackfriday.Run([]byte(input), blackfriday.WithExtensions(extensions), blackfriday.WithRenderer(renderer))) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,7 +8,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/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" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -81,8 +81,8 @@ func (b *Btelegram) Send(msg config.Message) (string, error) { | |||||||
| 	// 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) { | ||||||
| 			if _, err := b.sendMessage(chatid, rmsg.Username, rmsg.Text); err != nil { | 			if _, msgErr := b.sendMessage(chatid, rmsg.Username, rmsg.Text); msgErr != nil { | ||||||
| 				b.Log.Errorf("sendMessage failed: %s", err) | 				b.Log.Errorf("sendMessage failed: %s", msgErr) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// check if we have files to upload (from slack, telegram or mattermost) | 		// check if we have files to upload (from slack, telegram or mattermost) | ||||||
| @@ -97,7 +97,14 @@ func (b *Btelegram) Send(msg config.Message) (string, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Post normal message | 	// Post normal message | ||||||
| 	return b.sendMessage(chatid, msg.Username, msg.Text) | 	// TODO: recheck it. | ||||||
|  | 	// Ignore empty text field needs for prevent double messages from whatsapp to telegram | ||||||
|  | 	// when sending media with text caption | ||||||
|  | 	if msg.Text != "" { | ||||||
|  | 		return b.sendMessage(chatid, msg.Username, msg.Text) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return "", nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Btelegram) getFileDirectURL(id string) string { | func (b *Btelegram) getFileDirectURL(id string) string { | ||||||
| @@ -124,6 +131,9 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, er | |||||||
| 		m.Text = username + html.EscapeString(text) | 		m.Text = username + html.EscapeString(text) | ||||||
| 		m.ParseMode = tgbotapi.ModeHTML | 		m.ParseMode = tgbotapi.ModeHTML | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview") | ||||||
|  |  | ||||||
| 	res, err := b.c.Send(m) | 	res, err := b.c.Send(m) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
|   | |||||||
| @@ -1,14 +1,15 @@ | |||||||
| package bwhatsapp | package bwhatsapp | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"mime" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/config" | 	"github.com/42wim/matterbridge/bridge/config" | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/helper" | ||||||
| 	"github.com/Rhymen/go-whatsapp" | 	"github.com/Rhymen/go-whatsapp" | ||||||
|  | 	"github.com/jpillora/backoff" | ||||||
| 	whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -21,7 +22,42 @@ Check: | |||||||
|  |  | ||||||
| // HandleError received from WhatsApp | // HandleError received from WhatsApp | ||||||
| func (b *Bwhatsapp) HandleError(err error) { | func (b *Bwhatsapp) HandleError(err error) { | ||||||
| 	b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types | 	// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843 | ||||||
|  | 	if strings.Contains(err.Error(), "error processing data: received invalid data") { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch err.(type) { | ||||||
|  | 	case *whatsapp.ErrConnectionClosed, *whatsapp.ErrConnectionFailed: | ||||||
|  | 		b.reconnect(err) | ||||||
|  | 	default: | ||||||
|  | 		switch err { | ||||||
|  | 		case whatsapp.ErrConnectionTimeout: | ||||||
|  | 			b.reconnect(err) | ||||||
|  | 		default: | ||||||
|  | 			b.Log.Errorf("%v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *Bwhatsapp) reconnect(err error) { | ||||||
|  | 	bf := &backoff.Backoff{ | ||||||
|  | 		Min:    time.Second, | ||||||
|  | 		Max:    5 * time.Minute, | ||||||
|  | 		Jitter: true, | ||||||
|  | 	} | ||||||
|  | 	for { | ||||||
|  | 		d := bf.Duration() | ||||||
|  | 		b.Log.Errorf("Connection failed, underlying error: %v", err) | ||||||
|  | 		b.Log.Infof("Waiting %s...", d) | ||||||
|  | 		time.Sleep(d) | ||||||
|  | 		b.Log.Info("Reconnecting...") | ||||||
|  | 		err := b.conn.Restore() | ||||||
|  | 		if err == nil { | ||||||
|  | 			bf.Reset() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // HandleTextMessage sent from WhatsApp, relay it to the brige | // HandleTextMessage sent from WhatsApp, relay it to the brige | ||||||
| @@ -35,16 +71,16 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones | 	messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones | ||||||
| 	groupJid := message.Info.RemoteJid | 	groupJID := message.Info.RemoteJid | ||||||
|  |  | ||||||
| 	senderJid := message.Info.SenderJid | 	senderJID := message.Info.SenderJid | ||||||
| 	if len(senderJid) == 0 { | 	if len(senderJID) == 0 { | ||||||
| 		// TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved | 		// TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved | ||||||
| 		senderJid = *message.Info.Source.Participant | 		senderJID = *message.Info.Source.Participant | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// translate sender's Jid to the nicest username we can get | 	// translate sender's JID to the nicest username we can get | ||||||
| 	senderName := b.getSenderName(senderJid) | 	senderName := b.getSenderName(senderJID) | ||||||
| 	if senderName == "" { | 	if senderName == "" { | ||||||
| 		senderName = "Someone" // don't expose telephone number | 		senderName = "Someone" // don't expose telephone number | ||||||
| 	} | 	} | ||||||
| @@ -52,12 +88,12 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | |||||||
| 	extText := message.Info.Source.Message.ExtendedTextMessage | 	extText := message.Info.Source.Message.ExtendedTextMessage | ||||||
| 	if extText != nil && extText.ContextInfo != nil && extText.ContextInfo.MentionedJid != nil { | 	if extText != nil && extText.ContextInfo != nil && extText.ContextInfo.MentionedJid != nil { | ||||||
| 		// handle user mentions | 		// handle user mentions | ||||||
| 		for _, mentionedJid := range extText.ContextInfo.MentionedJid { | 		for _, mentionedJID := range extText.ContextInfo.MentionedJid { | ||||||
| 			numberAndSuffix := strings.SplitN(mentionedJid, "@", 2) | 			numberAndSuffix := strings.SplitN(mentionedJID, "@", 2) | ||||||
|  |  | ||||||
| 			// 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" | ||||||
| 			} | 			} | ||||||
| @@ -65,22 +101,22 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJid, b.Account) | 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||||
| 	rmsg := config.Message{ | 	rmsg := config.Message{ | ||||||
| 		UserID:    senderJid, | 		UserID:    senderJID, | ||||||
| 		Username:  senderName, | 		Username:  senderName, | ||||||
| 		Text:      message.Text, | 		Text:      message.Text, | ||||||
| 		Timestamp: messageTime, | 		Timestamp: messageTime, | ||||||
| 		Channel:   groupJid, | 		Channel:   groupJID, | ||||||
| 		Account:   b.Account, | 		Account:   b.Account, | ||||||
| 		Protocol:  b.Protocol, | 		Protocol:  b.Protocol, | ||||||
| 		Extra:     make(map[string][]interface{}), | 		Extra:     make(map[string][]interface{}), | ||||||
| 		//		ParentID: TODO, // TODO handle thread replies  // map from Info.QuotedMessageID string | 		//	ParentID: TODO, // TODO handle thread replies  // map from Info.QuotedMessageID string | ||||||
| 		//	Event     string    `json:"event"` | 		//	Event     string    `json:"event"` | ||||||
| 		//	Gateway   string  // will be added during message processing | 		//	Gateway   string  // will be added during message processing | ||||||
| 		ID: message.Info.Id} | 		ID: message.Info.Id} | ||||||
|  |  | ||||||
| 	if avatarURL, exists := b.userAvatars[senderJid]; exists { | 	if avatarURL, exists := b.userAvatars[senderJID]; exists { | ||||||
| 		rmsg.Avatar = avatarURL | 		rmsg.Avatar = avatarURL | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -88,11 +124,75 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | |||||||
| 	b.Remote <- rmsg | 	b.Remote <- rmsg | ||||||
| } | } | ||||||
|  |  | ||||||
| // | // HandleImageMessage sent from WhatsApp, relay it to the brige | ||||||
| //func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) { | func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) { | ||||||
| //	fmt.Println(message) // TODO implement | 	if message.Info.FromMe { // || !strings.Contains(strings.ToLower(message.Text), "@echo") { | ||||||
| //} | 		return | ||||||
| // | 	} | ||||||
|  |  | ||||||
|  | 	// whatsapp sends last messages to show context , cut them | ||||||
|  | 	if message.Info.Timestamp < b.startedAt { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones | ||||||
|  | 	groupJID := message.Info.RemoteJid | ||||||
|  |  | ||||||
|  | 	senderJID := message.Info.SenderJid | ||||||
|  | 	// if len(senderJid) == 0 { | ||||||
|  | 	//   // TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved | ||||||
|  | 	//   senderJid = *message.Info.Source.Participant | ||||||
|  | 	// } | ||||||
|  |  | ||||||
|  | 	// translate sender's Jid to the nicest username we can get | ||||||
|  | 	senderName := b.getSenderName(senderJID) | ||||||
|  | 	if senderName == "" { | ||||||
|  | 		senderName = "Someone" // don't expose telephone number | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||||
|  | 	rmsg := config.Message{ | ||||||
|  | 		UserID:    senderJID, | ||||||
|  | 		Username:  senderName, | ||||||
|  | 		Timestamp: messageTime, | ||||||
|  | 		Channel:   groupJID, | ||||||
|  | 		Account:   b.Account, | ||||||
|  | 		Protocol:  b.Protocol, | ||||||
|  | 		Extra:     make(map[string][]interface{}), | ||||||
|  | 		//  ParentID: TODO,      // TODO handle thread replies  // map from Info.QuotedMessageID string | ||||||
|  | 		//  Event     string    `json:"event"` | ||||||
|  | 		//  Gateway   string     // will be added during message processing | ||||||
|  | 		ID: message.Info.Id} | ||||||
|  |  | ||||||
|  | 	if avatarURL, exists := b.userAvatars[senderJID]; exists { | ||||||
|  | 		rmsg.Avatar = avatarURL | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Download and unencrypt content | ||||||
|  | 	data, err := message.Download() | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("%v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Get file extension by mimetype | ||||||
|  | 	fileExt, err := mime.ExtensionsByType(message.Type) | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Log.Errorf("%v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0]) | ||||||
|  |  | ||||||
|  | 	b.Log.Debugf("<= Image downloaded and unencrypted") | ||||||
|  |  | ||||||
|  | 	// Move file to bridge storage | ||||||
|  | 	helper.HandleDownloadData(b.Log, &rmsg, filename, message.Caption, "", &data, b.General) | ||||||
|  |  | ||||||
|  | 	b.Log.Debugf("<= Image Message is %#v", rmsg) | ||||||
|  | 	b.Remote <- rmsg | ||||||
|  | } | ||||||
|  |  | ||||||
| //func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) { | //func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) { | ||||||
| //	fmt.Println(message) // TODO implement | //	fmt.Println(message) // TODO implement | ||||||
| //} | //} | ||||||
|   | |||||||
| @@ -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/Rhymen/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 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,20 +1,20 @@ | |||||||
| package bwhatsapp | package bwhatsapp | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"mime" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"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/Rhymen/go-whatsapp" | ||||||
|  |  | ||||||
| 	whatsappExt "maunium.net/go/mautrix-whatsapp/whatsapp-ext" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -29,10 +29,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 | ||||||
| @@ -69,13 +67,12 @@ func (b *Bwhatsapp) Connect() error { | |||||||
| 	// https://github.com/Rhymen/go-whatsapp#creating-a-connection | 	// https://github.com/Rhymen/go-whatsapp#creating-a-connection | ||||||
| 	b.Log.Debugln("Connecting to WhatsApp..") | 	b.Log.Debugln("Connecting to WhatsApp..") | ||||||
| 	conn, err := whatsapp.NewConn(20 * time.Second) | 	conn, err := whatsapp.NewConn(20 * time.Second) | ||||||
|  | 	conn.SetClientVersion(0, 4, 1307) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return errors.New("failed to connect to WhatsApp: " + err.Error()) | 		return errors.New("failed to connect to WhatsApp: " + err.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 +86,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 +127,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) | ||||||
|  |  | ||||||
| @@ -237,6 +234,66 @@ func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Post a document message from the bridge to WhatsApp | ||||||
|  | func (b *Bwhatsapp) PostDocumentMessage(msg config.Message, filetype string) (string, error) { | ||||||
|  | 	fi := msg.Extra["file"][0].(config.FileInfo) | ||||||
|  |  | ||||||
|  | 	// Post document message | ||||||
|  | 	message := whatsapp.DocumentMessage{ | ||||||
|  | 		Info: whatsapp.MessageInfo{ | ||||||
|  | 			RemoteJid: msg.Channel, | ||||||
|  | 		}, | ||||||
|  | 		Title:    fi.Name, | ||||||
|  | 		FileName: fi.Name, | ||||||
|  | 		Type:     filetype, | ||||||
|  | 		Content:  bytes.NewReader(*fi.Data), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.Log.Debugf("=> Sending %#v", msg) | ||||||
|  |  | ||||||
|  | 	// create message ID | ||||||
|  | 	// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented | ||||||
|  | 	idBytes := make([]byte, 10) | ||||||
|  | 	if _, err := rand.Read(idBytes); err != nil { | ||||||
|  | 		b.Log.Warn(err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	message.Info.Id = strings.ToUpper(hex.EncodeToString(idBytes)) | ||||||
|  | 	_, err := b.conn.Send(message) | ||||||
|  |  | ||||||
|  | 	return message.Info.Id, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Post an image message from the bridge to WhatsApp | ||||||
|  | // Handle, for sure image/jpeg, image/png and image/gif MIME types | ||||||
|  | func (b *Bwhatsapp) PostImageMessage(msg config.Message, filetype string) (string, error) { | ||||||
|  | 	fi := msg.Extra["file"][0].(config.FileInfo) | ||||||
|  |  | ||||||
|  | 	// Post image message | ||||||
|  | 	message := whatsapp.ImageMessage{ | ||||||
|  | 		Info: whatsapp.MessageInfo{ | ||||||
|  | 			RemoteJid: msg.Channel, | ||||||
|  | 		}, | ||||||
|  | 		Type:    filetype, | ||||||
|  | 		Caption: msg.Username + fi.Comment, | ||||||
|  | 		Content: bytes.NewReader(*fi.Data), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.Log.Debugf("=> Sending %#v", msg) | ||||||
|  |  | ||||||
|  | 	// create message ID | ||||||
|  | 	// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented | ||||||
|  | 	idBytes := make([]byte, 10) | ||||||
|  | 	if _, err := rand.Read(idBytes); err != nil { | ||||||
|  | 		b.Log.Warn(err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	message.Info.Id = strings.ToUpper(hex.EncodeToString(idBytes)) | ||||||
|  | 	_, err := b.conn.Send(message) | ||||||
|  |  | ||||||
|  | 	return message.Info.Id, err | ||||||
|  | } | ||||||
|  |  | ||||||
| // Send a message from the bridge to WhatsApp | // Send a message from the bridge to WhatsApp | ||||||
| // Required implementation of the Bridger interface | // Required implementation of the Bridger interface | ||||||
| // https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16 | // https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16 | ||||||
| @@ -266,18 +323,25 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) { | |||||||
| 		// TODO handle edit as a message reply with updated text | 		// TODO handle edit as a message reply with updated text | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	//// TODO Handle Upload a file | 	// Handle Upload a file | ||||||
| 	//if msg.Extra != nil { | 	if msg.Extra["file"] != nil { | ||||||
| 	//	for _, rmsg := range helper.HandleExtra(&msg, b.General) { | 		fi := msg.Extra["file"][0].(config.FileInfo) | ||||||
| 	//		b.c.SendMessage(roomID, rmsg.Username+rmsg.Text) | 		filetype := mime.TypeByExtension(filepath.Ext(fi.Name)) | ||||||
| 	//	} |  | ||||||
| 	//	if len(msg.Extra["file"]) > 0 { | 		b.Log.Debugf("Extra file is %#v", filetype) | ||||||
| 	//		return b.handleUploadFile(&msg, roomID) |  | ||||||
| 	//	} | 		// TODO: add different types | ||||||
| 	//} | 		// TODO: add webp conversion | ||||||
|  | 		switch filetype { | ||||||
|  | 		case "image/jpeg", "image/png", "image/gif": | ||||||
|  | 			return b.PostImageMessage(msg, filetype) | ||||||
|  | 		default: | ||||||
|  | 			return b.PostDocumentMessage(msg, filetype) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Post text message | 	// Post text message | ||||||
| 	text := whatsapp.TextMessage{ | 	message := whatsapp.TextMessage{ | ||||||
| 		Info: whatsapp.MessageInfo{ | 		Info: whatsapp.MessageInfo{ | ||||||
| 			RemoteJid: msg.Channel, // which equals to group id | 			RemoteJid: msg.Channel, // which equals to group id | ||||||
| 		}, | 		}, | ||||||
| @@ -288,15 +352,14 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) { | |||||||
|  |  | ||||||
| 	// create message ID | 	// create message ID | ||||||
| 	// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented | 	// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented | ||||||
| 	bytes := make([]byte, 10) | 	idBytes := make([]byte, 10) | ||||||
| 	if _, err := rand.Read(bytes); err != nil { | 	if _, err := rand.Read(idBytes); err != nil { | ||||||
| 		b.Log.Warn(err.Error()) | 		b.Log.Warn(err.Error()) | ||||||
| 	} | 	} | ||||||
| 	text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes)) | 	message.Info.Id = strings.ToUpper(hex.EncodeToString(idBytes)) | ||||||
|  | 	_, err := b.conn.Send(message) | ||||||
|  |  | ||||||
| 	err := b.conn.Send(text) | 	return message.Info.Id, err | ||||||
|  |  | ||||||
| 	return text.Info.Id, err |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // TODO do we want that? to allow login with QR code from a bridged channel? https://github.com/tulir/mautrix-whatsapp/blob/513eb18e2d59bada0dd515ee1abaaf38a3bfe3d5/commands.go#L76 | // TODO do we want that? to allow login with QR code from a bridged channel? https://github.com/tulir/mautrix-whatsapp/blob/513eb18e2d59bada0dd515ee1abaaf38a3bfe3d5/commands.go#L76 | ||||||
|   | |||||||
| @@ -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,61 @@ 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) | 	if !strings.Contains(b.GetString("Jid"), "@") { | ||||||
| 	tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify") | 		return fmt.Errorf("the Jid %s doesn't contain an @", b.GetString("Jid")) | ||||||
| 	tc.ServerName = strings.Split(b.GetString("Server"), ":")[0] | 	} | ||||||
|  | 	tc := &tls.Config{ | ||||||
|  | 		ServerName:         strings.Split(b.GetString("Jid"), "@")[1], | ||||||
|  | 		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 +131,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 +190,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 +202,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 +238,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 +267,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 +346,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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import ( | |||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -18,12 +19,11 @@ type Bzulip struct { | |||||||
| 	bot     *gzb.Bot | 	bot     *gzb.Bot | ||||||
| 	streams map[int]string | 	streams map[int]string | ||||||
| 	*bridge.Config | 	*bridge.Config | ||||||
| 	channelToTopic map[string]string |  | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| } | } | ||||||
|  |  | ||||||
| func New(cfg *bridge.Config) bridge.Bridger { | func New(cfg *bridge.Config) bridge.Bridger { | ||||||
| 	return &Bzulip{Config: cfg, streams: make(map[int]string), channelToTopic: make(map[string]string)} | 	return &Bzulip{Config: cfg, streams: make(map[int]string)} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bzulip) Connect() error { | func (b *Bzulip) Connect() error { | ||||||
| @@ -48,9 +48,6 @@ func (b *Bzulip) Disconnect() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bzulip) JoinChannel(channel config.ChannelInfo) error { | func (b *Bzulip) JoinChannel(channel config.ChannelInfo) error { | ||||||
| 	b.Lock() |  | ||||||
| 	defer b.Unlock() |  | ||||||
| 	b.channelToTopic[channel.Name] = channel.Options.Topic |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -116,11 +113,13 @@ func (b *Bzulip) handleQueue() error { | |||||||
| 		case gzb.BadEventQueueError: | 		case gzb.BadEventQueueError: | ||||||
| 			b.Log.Info("got a bad event queue id error, reconnecting") | 			b.Log.Info("got a bad event queue id error, reconnecting") | ||||||
| 			b.bot.Queues = nil | 			b.bot.Queues = nil | ||||||
| 			b.q, err = b.bot.RegisterAll() | 			for { | ||||||
| 			if err != nil { | 				b.q, err = b.bot.RegisterAll() | ||||||
| 				b.Log.Errorf("reconnecting failed: %s. Sleeping 10 seconds", err) | 				if err != nil { | ||||||
| 				time.Sleep(time.Second * 10) | 					b.Log.Errorf("reconnecting failed: %s. Sleeping 10 seconds", err) | ||||||
| 				continue | 					time.Sleep(time.Second * 10) | ||||||
|  | 				} | ||||||
|  | 				break | ||||||
| 			} | 			} | ||||||
| 		case gzb.HeartbeatError: | 		case gzb.HeartbeatError: | ||||||
| 			b.Log.Debug("heartbeat received.") | 			b.Log.Debug("heartbeat received.") | ||||||
| @@ -136,7 +135,14 @@ func (b *Bzulip) handleQueue() error { | |||||||
| 			if m.SenderEmail == b.GetString("login") { | 			if m.SenderEmail == b.GetString("login") { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			rmsg := config.Message{Username: m.SenderFullName, Text: m.Content, Channel: b.getChannel(m.StreamID), Account: b.Account, UserID: strconv.Itoa(m.SenderID), Avatar: m.AvatarURL} | 			rmsg := config.Message{ | ||||||
|  | 				Username: m.SenderFullName, | ||||||
|  | 				Text:     m.Content, | ||||||
|  | 				Channel:  b.getChannel(m.StreamID) + "/topic:" + m.Subject, | ||||||
|  | 				Account:  b.Account, | ||||||
|  | 				UserID:   strconv.Itoa(m.SenderID), | ||||||
|  | 				Avatar:   m.AvatarURL, | ||||||
|  | 			} | ||||||
| 			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 | ||||||
| @@ -147,12 +153,11 @@ func (b *Bzulip) handleQueue() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bzulip) sendMessage(msg config.Message) (string, error) { | func (b *Bzulip) sendMessage(msg config.Message) (string, error) { | ||||||
| 	topic := "matterbridge" | 	topic := "" | ||||||
| 	if b.GetString("topic") != "" { | 	if strings.Contains(msg.Channel, "/topic:") { | ||||||
| 		topic = b.GetString("topic") | 		res := strings.Split(msg.Channel, "/topic:") | ||||||
| 	} | 		topic = res[1] | ||||||
| 	if res := b.getTopic(msg.Channel); res != "" { | 		msg.Channel = res[0] | ||||||
| 		topic = res |  | ||||||
| 	} | 	} | ||||||
| 	m := gzb.Message{ | 	m := gzb.Message{ | ||||||
| 		Stream:  msg.Channel, | 		Stream:  msg.Channel, | ||||||
| @@ -200,9 +205,3 @@ func (b *Bzulip) handleUploadFile(msg *config.Message) (string, error) { | |||||||
| 	} | 	} | ||||||
| 	return "", nil | 	return "", nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *Bzulip) getTopic(channel string) string { |  | ||||||
| 	b.RLock() |  | ||||||
| 	defer b.RUnlock() |  | ||||||
| 	return b.channelToTopic[channel] |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										256
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										256
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,4 +1,241 @@ | |||||||
| # v1.14.0-rc1 | # v1.17.1 | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  |  | ||||||
|  | - docker: Remove build dependencies from final image (multistage build) #1057 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | - general: Don't transmit typing events from ourselves #1056 | ||||||
|  | - general: Add support for build tags #1054 | ||||||
|  | - discord: Strip extra info from emotes (discord) #1052 | ||||||
|  | - msteams: fix macos build: Update vendor yaegashi/msgraph.go to v0.1.2 #1036 | ||||||
|  | - whatsapp: Update client version whatsapp. Fixes #1061 #1062 | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @awigen, @qaisjp, @42wim | ||||||
|  |  | ||||||
|  | # v1.17.0 | ||||||
|  |  | ||||||
|  | ## New features | ||||||
|  |  | ||||||
|  | - msteams: new protocol added. Add initial Microsoft Teams support #967 | ||||||
|  |   See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup for a complete walkthrough | ||||||
|  | - discord: Add ability to procure avatars from the destination bridge #1000 | ||||||
|  | - matrix: Add support for avatars from matrix. #1007 | ||||||
|  | - general: support JSON and YAML config formats #1045 | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  |  | ||||||
|  | - discord: Check only bridged channels for PermManageWebhooks #1001 | ||||||
|  | - irc: Be less lossy when throttling IRC messages #1004 | ||||||
|  | - keybase: updated library #1002, #1019 | ||||||
|  | - matrix: Rebase gomatrix vendor with upstream #1006 | ||||||
|  | - slack: Use upstream slack-go/slack again #1018 | ||||||
|  | - slack: Ignore ConnectingEvent #1041 | ||||||
|  | - slack: use blocks not attachments #1048 | ||||||
|  | - sshchat: Update vendor shazow/ssh-chat #1029 | ||||||
|  | - telegram: added markdownv2 mode for telegram #1037 | ||||||
|  | - whatsapp: Implement basic reconnect (whatsapp). Fixes #987 #1003 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | - discord: Fix webhook permission checks sometimes failing #1043 | ||||||
|  | - discord: Fix #1027: warning when handling inbound webhooks #1044 | ||||||
|  | - discord: Fix duplicate separator on empty description/url (discord) #1035 | ||||||
|  | - matrix: Fix issue with underscores in links #999 | ||||||
|  | - slack: Fix #1039: messages sent to Slack being synced back #1046 | ||||||
|  | - telegram: Make avatars download work with mediaserverdownload (telegram). Fixes #920  | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @qaisjp, @jakubgs, @burner1024, @notpushkin, @MartijnBraam, @42wim | ||||||
|  |  | ||||||
|  | # v1.16.5 | ||||||
|  |  | ||||||
|  | - Fix version bump | ||||||
|  |  | ||||||
|  | # v1.16.4 | ||||||
|  |  | ||||||
|  | ## New features | ||||||
|  |  | ||||||
|  | - whatsapp: Add support for WhatsApp media (jpeg/png/gif) bridging (#974) | ||||||
|  | - telegram: Add QuoteLengthLimit option (telegram) fixes #963 (#985) | ||||||
|  | - telegram: Add DisableWebPagePreview option (telegram). Closes #980 (#994) | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  |  | ||||||
|  | - general: update dependencies | ||||||
|  | - tengo: update to tengo v2 | ||||||
|  | - general: Add Docker Compose configuration (#990) | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | - general: Fail with message instead of panic. #988 (#991) | ||||||
|  | - telegram: Add extra mimetypes to docker image. Fixes #969 | ||||||
|  | - discord: Fix channel ID problem with multiple gateways (discord). Fixes #953 (#977) | ||||||
|  | - discord: Show file comment in webhook if normal message is empty (discord). Fixes #962 (#995) | ||||||
|  | - matrix: Fix parsing issues - Disable smartypants in markdown parser. Fixes #989, #983 (#993) | ||||||
|  | - sshchat: Fix duplicated messages (sshchat). Fixes #950 (#996) | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @jwflory, @42wim, @pbek, @Humorhenker, @c0ncord2, @glazzara | ||||||
|  |  | ||||||
|  | # v1.16.3 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | - slack: Fix issues with ratelimiting #959 | ||||||
|  | - mattermost: Fix bug when using webhookURL and login/token together #960 | ||||||
|  |  | ||||||
|  | # v1.16.2 | ||||||
|  |  | ||||||
|  | ## New features | ||||||
|  |  | ||||||
|  | - keybase: Add support for receiving attachments (keybase) (#923) | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  |  | ||||||
|  | - general: Switch to new emoji library kyokomi/emoji (#948) | ||||||
|  | - general: Update markdown parsing library to github.com/gomarkdown/markdown (#944) | ||||||
|  | - ssh-chat: Update shazow/ssh-chat dependency (#947) | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | - slack: Fix issues with the slack block kit API #937 (#943). | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @42wim, @bmpickford, @goncalor | ||||||
|  |  | ||||||
|  | # v1.16.1 | ||||||
|  |  | ||||||
|  | ## New features | ||||||
|  |  | ||||||
|  | * rocketchat: add token support #892 | ||||||
|  | * matrix: Add support for uploading application/x and audio/x (matrix). #929 | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  |  | ||||||
|  | * general: Do configuration validation on start-up. Fixes #888 | ||||||
|  | * general: updated vendored libraries (discord/whatsapp) #932 | ||||||
|  | * discord: user typing messages #914 | ||||||
|  | * slack: Convert slack bold/strike to correct markdown (slack). Fixes #918 | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | * discord: fix Failed to fetch information for members message. #894 | ||||||
|  | * discord: remove obsolete file upload links (discord). #931 | ||||||
|  | * slack: suppress unhandled HelloEvent message #913 | ||||||
|  | * mattermost: Fix panic on WebhookURL only setting (mattermost). #917 | ||||||
|  | * matrix: fix corrupted links between slack and matrix #924 | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @qaisjp, @hramrach, @42wim | ||||||
|  |  | ||||||
|  | # v1.16.0 | ||||||
|  |  | ||||||
|  | ## New features | ||||||
|  |  | ||||||
|  | * keybase: new protocol added. Add initial Keybase Chat support #877 Thanks to @hyperobject | ||||||
|  | * discord: Support webhook files in discord #872 | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  |  | ||||||
|  | * general: update dependencies | ||||||
|  |  | ||||||
|  | ## Bugfix | ||||||
|  |  | ||||||
|  | * discord: Underscores from Discord don't arrive correctly #864 | ||||||
|  | * xmpp: Fix possible panic at startup of the XMPP bridge #869 | ||||||
|  | * mattermost: Make getChannelIdTeam behave like GetChannelId for groups (mattermost) #873 | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @hyperobject, @42wim, @bucko909, @MOZGIII | ||||||
|  |  | ||||||
|  | # v1.15.1 | ||||||
|  |  | ||||||
|  | ## New features | ||||||
|  | * discord: Support webhook message deletions (discord) (#853) | ||||||
|  |  | ||||||
|  | ## 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 | ||||||
|  |  | ||||||
|  | This release couldn't exist without the following contributors: | ||||||
|  | @42wim, @qaisjp, @joohoi | ||||||
|  |  | ||||||
|  | # 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 | ||||||
|  | ## Bugfix | ||||||
|  | * slack: Fix crash double unlock (slack) (#771) | ||||||
|  |  | ||||||
|  | # v1.14.0 | ||||||
|  |  | ||||||
|  | ## Breaking | ||||||
|  | * zulip: Need to specify /topic:mytopic for channel configuration (zulip). (#751) | ||||||
|  |  | ||||||
| ## New features | ## New features | ||||||
| * whatsapp: new protocol added. Add initial WhatsApp support (#711) Thanks to @KrzysztofMadejski | * whatsapp: new protocol added. Add initial WhatsApp support (#711) Thanks to @KrzysztofMadejski | ||||||
| @@ -10,18 +247,29 @@ | |||||||
| * rocketchat: add support for the rocketchat API. Sending to rocketchat now supports uploading of files, editing and deleting of messages. | * rocketchat: add support for the rocketchat API. Sending to rocketchat now supports uploading of files, editing and deleting of messages. | ||||||
| * discord: Support join/leaves from discord. Closes #654 (#721) | * discord: Support join/leaves from discord. Closes #654 (#721) | ||||||
| * discord: Allow sending discriminator with Discord username (#726). See `UseDiscriminator` in matterbridge.toml.sample | * discord: Allow sending discriminator with Discord username (#726). See `UseDiscriminator` in matterbridge.toml.sample | ||||||
| * zulip: Allow zulip bridge to specify topic per channel. Closes #701 (#723). See `Topic` in matterbridge.toml.sample |  | ||||||
| * slack: Add extra debug option (slack). See `Debug` in the slack section in matterbridge.toml.sample | * slack: Add extra debug option (slack). See `Debug` in the slack section in matterbridge.toml.sample | ||||||
|  | * telegram: Add support for URL in messageEntities (telegram). Fixes #735 (#736) | ||||||
|  | * telegram: Add MediaConvertWebPToPNG option (telegram). (#741). See `MediaConvertWebPToPNG` in matterbridge.toml.sample | ||||||
|  |  | ||||||
|  | ## Enhancements | ||||||
|  | * general: Fail gracefully on incorrect human input. Fixes #739 (#740) | ||||||
|  | * matrix: Detect html nicks in RemoteNickFormat (matrix). Fixes #696 (#719) | ||||||
|  | * matrix: Send notices on join/parts (matrix). Fixes #712 (#716) | ||||||
|  |  | ||||||
| ## Bugfix | ## Bugfix | ||||||
|  | * general: Handle file upload/download only once for each message (#742) | ||||||
| * zulip: Fix error handling on bad event queue id (zulip). Closes #694 | * zulip: Fix error handling on bad event queue id (zulip). Closes #694 | ||||||
|  | * zulip: Keep reconnecting until succeed (zulip) (#737) | ||||||
| * irc: add support for (older) unrealircd versions. #708 | * irc: add support for (older) unrealircd versions. #708 | ||||||
| * irc: Support quits from irc correctly. Fixes #722 (#724) | * irc: Support quits from irc correctly. Fixes #722 (#724) | ||||||
| * matrix: Send username when uploading video/images (matrix). Fixes #715 (#717) | * matrix: Send username when uploading video/images (matrix). Fixes #715 (#717) | ||||||
| * matrix: Send notices on join/parts (matrix). Fixes #712 (#716) | * matrix: Trim <p> and </p> tags (matrix). Closes #686 (#753) | ||||||
| * matrix: Detect html nicks in RemoteNickFormat (matrix). Fixes #696 (#719) |  | ||||||
| * slack: Hint at thread replies when messages are unthreaded (slack) (#684) | * slack: Hint at thread replies when messages are unthreaded (slack) (#684) | ||||||
|  | * slack: Fix race-condition in populateUser() (#767) | ||||||
| * xmpp: Do not send topic changes on connect (xmpp). Fixes #732 (#733) | * xmpp: Do not send topic changes on connect (xmpp). Fixes #732 (#733) | ||||||
|  | * telegram: Fix regression in HTML handling (telegram). Closes #734 | ||||||
|  | * discord: Do not relay any bot messages (discord) (#743) | ||||||
|  | * rocketchat: Do not send duplicate messages (rocketchat). Fixes #745 (#752) | ||||||
|  |  | ||||||
| ## Contributors | ## Contributors | ||||||
| This release couldn't exist without the following contributors: | This release couldn't exist without the following contributors: | ||||||
|   | |||||||
| @@ -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.14 || 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 | ||||||
							
								
								
									
										10
									
								
								contrib/outmessage-discordemoji.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								contrib/outmessage-discordemoji.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | text := import("text") | ||||||
|  |  | ||||||
|  | // if we're not sending to a discord bridge, | ||||||
|  | // then convert custom emoji tags into url's | ||||||
|  | if (inProtocol == "discord" && outProtocol != "discord") { | ||||||
|  |     rePNG := text.re_compile(`<:.*?:([0-9]+)>`) | ||||||
|  |     msgText=rePNG.replace(msgText,"https://cdn.discordapp.com/emojis/$1.png") | ||||||
|  |     reGIF := text.re_compile(`<a:.*?:([0-9]+)>`) | ||||||
|  |     msgText=reGIF.replace(msgText,"https://cdn.discordapp.com/emojis/$1.gif") | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								contrib/outmessage-irccolornick.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								contrib/outmessage-irccolornick.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | // See https://github.com/42wim/matterbridge/issues/881 | ||||||
|  | // Generates a colored nick for each msgUsername, with example to filter specific codes  | ||||||
|  |  | ||||||
|  | text := import("text") | ||||||
|  | fmt := import("fmt") | ||||||
|  | if outProtocol == "irc" { | ||||||
|  |     // generate a color for a nick, make sure it isn't 0 or 15 | ||||||
|  |     colorCode := len(msgUsername)+bytes(msgUsername)[0]%14 + 2 | ||||||
|  |     // example if we want to use colorCode 3 when we have calculated colorcode 14 | ||||||
|  |     if colorCode == 14 { | ||||||
|  |         colorCode = 3 | ||||||
|  |     } | ||||||
|  |     msgUsername=fmt.sprintf("\x03%02d%s\x0F", colorCode, msgUsername) | ||||||
|  | } | ||||||
							
								
								
									
										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) | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !noapi | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/42wim/matterbridge/bridge/api" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["api"] = api.New | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								gateway/bridgemap/bdiscord.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								gateway/bridgemap/bdiscord.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | // +build !nodiscord | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bdiscord "github.com/42wim/matterbridge/bridge/discord" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["discord"] = bdiscord.New | ||||||
|  | 	UserTypingSupport["discord"] = struct{}{} | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bgitter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bgitter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nogitter | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bgitter "github.com/42wim/matterbridge/bridge/gitter" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["gitter"] = bgitter.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/birc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/birc.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !noirc | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	birc "github.com/42wim/matterbridge/bridge/irc" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["irc"] = birc.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bkeybase.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bkeybase.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nokeybase | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bkeybase "github.com/42wim/matterbridge/bridge/keybase" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["keybase"] = bkeybase.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bmatrix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmatrix.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nomatrix | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bmatrix "github.com/42wim/matterbridge/bridge/matrix" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["matrix"] = bmatrix.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bmattermost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmattermost.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nomattermost | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bmattermost "github.com/42wim/matterbridge/bridge/mattermost" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["mattermost"] = bmattermost.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bmsteams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmsteams.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nomsteams | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bmsteams "github.com/42wim/matterbridge/bridge/msteams" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["msteams"] = bmsteams.New | ||||||
|  | } | ||||||
| @@ -2,36 +2,9 @@ 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/discord" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/gitter" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/irc" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/matrix" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/mattermost" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/rocketchat" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/slack" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/sshchat" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/steam" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/telegram" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/whatsapp" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/xmpp" |  | ||||||
| 	"github.com/42wim/matterbridge/bridge/zulip" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var FullMap = map[string]bridge.Factory{ | var ( | ||||||
| 	"api":          api.New, | 	FullMap           = map[string]bridge.Factory{} | ||||||
| 	"discord":      bdiscord.New, | 	UserTypingSupport = map[string]struct{}{} | ||||||
| 	"gitter":       bgitter.New, | ) | ||||||
| 	"irc":          birc.New, |  | ||||||
| 	"mattermost":   bmattermost.New, |  | ||||||
| 	"matrix":       bmatrix.New, |  | ||||||
| 	"rocketchat":   brocketchat.New, |  | ||||||
| 	"slack-legacy": bslack.NewLegacy, |  | ||||||
| 	"slack":        bslack.New, |  | ||||||
| 	"sshchat":      bsshchat.New, |  | ||||||
| 	"steam":        bsteam.New, |  | ||||||
| 	"telegram":     btelegram.New, |  | ||||||
| 	"whatsapp":     bwhatsapp.New, |  | ||||||
| 	"xmpp":         bxmpp.New, |  | ||||||
| 	"zulip":        bzulip.New, |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !norocketchat | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["rocketchat"] = brocketchat.New | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | // +build !noslack | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bslack "github.com/42wim/matterbridge/bridge/slack" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["slack-legacy"] = bslack.NewLegacy | ||||||
|  | 	FullMap["slack"] = bslack.New | ||||||
|  | 	UserTypingSupport["slack"] = struct{}{} | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nosshchat | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bsshchat "github.com/42wim/matterbridge/bridge/sshchat" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["sshchat"] = bsshchat.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nosteam | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bsteam "github.com/42wim/matterbridge/bridge/steam" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["steam"] = bsteam.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !notelegram | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	btelegram "github.com/42wim/matterbridge/bridge/telegram" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["telegram"] = btelegram.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nowhatsapp | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["whatsapp"] = bwhatsapp.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !noxmpp | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bxmpp "github.com/42wim/matterbridge/bridge/xmpp" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["xmpp"] = bxmpp.New | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | // +build !nozulip | ||||||
|  |  | ||||||
|  | package bridgemap | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	bzulip "github.com/42wim/matterbridge/bridge/zulip" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	FullMap["zulip"] = bzulip.New | ||||||
|  | } | ||||||
| @@ -9,9 +9,11 @@ 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/d5/tengo/script" | 	"github.com/42wim/matterbridge/internal" | ||||||
|  | 	"github.com/d5/tengo/v2" | ||||||
|  | 	"github.com/d5/tengo/v2/stdlib" | ||||||
| 	lru "github.com/hashicorp/golang-lru" | 	lru "github.com/hashicorp/golang-lru" | ||||||
| 	"github.com/peterhellberg/emojilib" | 	"github.com/matterbridge/emoji" | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -83,6 +85,7 @@ func (gw *Gateway) FindCanonicalMsgID(protocol string, mID string) string { | |||||||
| func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | ||||||
| 	br := gw.Router.getBridge(cfg.Account) | 	br := gw.Router.getBridge(cfg.Account) | ||||||
| 	if br == nil { | 	if br == nil { | ||||||
|  | 		gw.checkConfig(cfg) | ||||||
| 		br = bridge.New(cfg) | 		br = bridge.New(cfg) | ||||||
| 		br.Config = gw.Router.Config | 		br.Config = gw.Router.Config | ||||||
| 		br.General = &gw.BridgeValues().General | 		br.General = &gw.BridgeValues().General | ||||||
| @@ -92,6 +95,9 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | |||||||
| 			Bridge: br, | 			Bridge: br, | ||||||
| 		} | 		} | ||||||
| 		// add the actual bridger for this protocol to this bridge using the bridgeMap | 		// add the actual bridger for this protocol to this bridge using the bridgeMap | ||||||
|  | 		if _, ok := gw.Router.BridgeMap[br.Protocol]; !ok { | ||||||
|  | 			gw.logger.Fatalf("Incorrect protocol %s specified in gateway configuration %s, exiting.", br.Protocol, cfg.Account) | ||||||
|  | 		} | ||||||
| 		br.Bridger = gw.Router.BridgeMap[br.Protocol](brconfig) | 		br.Bridger = gw.Router.BridgeMap[br.Protocol](brconfig) | ||||||
| 	} | 	} | ||||||
| 	gw.mapChannelsToBridge(br) | 	gw.mapChannelsToBridge(br) | ||||||
| @@ -99,6 +105,19 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (gw *Gateway) checkConfig(cfg *config.Bridge) { | ||||||
|  | 	match := false | ||||||
|  | 	for _, key := range gw.Router.Config.Viper().AllKeys() { | ||||||
|  | 		if strings.HasPrefix(key, cfg.Account) { | ||||||
|  | 			match = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !match { | ||||||
|  | 		gw.logger.Fatalf("Account %s defined in gateway %s but no configuration found, exiting.", cfg.Account, gw.Name) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // AddConfig associates a new configuration with the gateway object. | // AddConfig associates a new configuration with the gateway object. | ||||||
| func (gw *Gateway) AddConfig(cfg *config.Gateway) error { | func (gw *Gateway) AddConfig(cfg *config.Gateway) error { | ||||||
| 	gw.Name = cfg.Name | 	gw.Name = cfg.Name | ||||||
| @@ -156,6 +175,10 @@ func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) { | |||||||
| 			gw.logger.Errorf("Mattermost channels do not start with a #: remove the # in %s", br.Channel) | 			gw.logger.Errorf("Mattermost channels do not start with a #: remove the # in %s", br.Channel) | ||||||
| 			os.Exit(1) | 			os.Exit(1) | ||||||
| 		} | 		} | ||||||
|  | 		if strings.HasPrefix(br.Account, "zulip.") && !strings.Contains(br.Channel, "/topic:") { | ||||||
|  | 			gw.logger.Errorf("Breaking change, since matterbridge 1.14.0 zulip channels need to specify the topic with channel/topic:mytopic in %s of %s", br.Channel, br.Account) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
| 		ID := br.Channel + br.Account | 		ID := br.Channel + br.Account | ||||||
| 		if _, ok := gw.Channels[ID]; !ok { | 		if _, ok := gw.Channels[ID]; !ok { | ||||||
| 			channel := &config.ChannelInfo{ | 			channel := &config.ChannelInfo{ | ||||||
| @@ -204,23 +227,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 | ||||||
| @@ -300,8 +306,6 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) string { | func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) string { | ||||||
| 	br := gw.Bridges[msg.Account] |  | ||||||
| 	msg.Protocol = br.Protocol |  | ||||||
| 	if dest.GetBool("StripNick") { | 	if dest.GetBool("StripNick") { | ||||||
| 		re := regexp.MustCompile("[^a-zA-Z0-9]+") | 		re := regexp.MustCompile("[^a-zA-Z0-9]+") | ||||||
| 		msg.Username = re.ReplaceAllString(msg.Username, "") | 		msg.Username = re.ReplaceAllString(msg.Username, "") | ||||||
| @@ -309,6 +313,7 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri | |||||||
| 	nick := dest.GetString("RemoteNickFormat") | 	nick := dest.GetString("RemoteNickFormat") | ||||||
|  |  | ||||||
| 	// loop to replace nicks | 	// loop to replace nicks | ||||||
|  | 	br := gw.Bridges[msg.Account] | ||||||
| 	for _, outer := range br.GetStringSlice2D("ReplaceNicks") { | 	for _, outer := range br.GetStringSlice2D("ReplaceNicks") { | ||||||
| 		search := outer[0] | 		search := outer[0] | ||||||
| 		replace := outer[1] | 		replace := outer[1] | ||||||
| @@ -340,6 +345,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 | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -356,9 +366,12 @@ 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 = emoji.Sprint(msg.Text) | ||||||
|  |  | ||||||
| 	br := gw.Bridges[msg.Account] | 	br := gw.Bridges[msg.Account] | ||||||
| 	// loop to replace messages | 	// loop to replace messages | ||||||
| @@ -430,6 +443,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" { | ||||||
| @@ -495,7 +513,8 @@ func modifyMessageTengo(filename string, msg *config.Message) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	s := script.New(res) | 	s := tengo.NewScript(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) | ||||||
| @@ -511,3 +530,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 := tengo.NewScript(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 := tengo.NewScript(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 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -15,10 +15,15 @@ import ( | |||||||
|  |  | ||||||
| var testconfig = []byte(` | var testconfig = []byte(` | ||||||
| [irc.freenode] | [irc.freenode] | ||||||
|  | server="" | ||||||
| [mattermost.test] | [mattermost.test] | ||||||
|  | server="" | ||||||
| [gitter.42wim] | [gitter.42wim] | ||||||
|  | server="" | ||||||
| [discord.test] | [discord.test] | ||||||
|  | server="" | ||||||
| [slack.test] | [slack.test] | ||||||
|  | server="" | ||||||
|  |  | ||||||
| [[gateway]] | [[gateway]] | ||||||
|     name = "bridge1" |     name = "bridge1" | ||||||
| @@ -44,10 +49,15 @@ var testconfig = []byte(` | |||||||
|  |  | ||||||
| var testconfig2 = []byte(` | var testconfig2 = []byte(` | ||||||
| [irc.freenode] | [irc.freenode] | ||||||
|  | server="" | ||||||
| [mattermost.test] | [mattermost.test] | ||||||
|  | server="" | ||||||
| [gitter.42wim] | [gitter.42wim] | ||||||
|  | server="" | ||||||
| [discord.test] | [discord.test] | ||||||
|  | server="" | ||||||
| [slack.test] | [slack.test] | ||||||
|  | server="" | ||||||
|  |  | ||||||
| [[gateway]] | [[gateway]] | ||||||
|     name = "bridge1" |     name = "bridge1" | ||||||
| @@ -87,8 +97,11 @@ var testconfig2 = []byte(` | |||||||
|  |  | ||||||
| var testconfig3 = []byte(` | var testconfig3 = []byte(` | ||||||
| [irc.zzz] | [irc.zzz] | ||||||
|  | server="" | ||||||
| [telegram.zzz] | [telegram.zzz] | ||||||
|  | server="" | ||||||
| [slack.zzz] | [slack.zzz] | ||||||
|  | server="" | ||||||
| [[gateway]] | [[gateway]] | ||||||
| name="bridge" | name="bridge" | ||||||
| enable=true | enable=true | ||||||
| @@ -176,7 +189,6 @@ func TestNewRouter(t *testing.T) { | |||||||
| 	assert.Equal(t, 1, len(r.Gateways)) | 	assert.Equal(t, 1, len(r.Gateways)) | ||||||
| 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges)) | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges)) | ||||||
| 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels)) | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels)) | ||||||
|  |  | ||||||
| 	r = maketestRouter(testconfig2) | 	r = maketestRouter(testconfig2) | ||||||
| 	assert.Equal(t, 2, len(r.Gateways)) | 	assert.Equal(t, 2, len(r.Gateways)) | ||||||
| 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges)) | 	assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges)) | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -59,8 +59,14 @@ func NewRouter(rootLogger *logrus.Logger, cfg config.Config, bridgeMap map[strin | |||||||
| // between them. | // between them. | ||||||
| func (r *Router) Start() error { | func (r *Router) Start() error { | ||||||
| 	m := make(map[string]*bridge.Bridge) | 	m := make(map[string]*bridge.Bridge) | ||||||
|  | 	if len(r.Gateways) == 0 { | ||||||
|  | 		return fmt.Errorf("no [[gateway]] configured. See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for more info") | ||||||
|  | 	} | ||||||
| 	for _, gw := range r.Gateways { | 	for _, gw := range r.Gateways { | ||||||
| 		r.logger.Infof("Parsing gateway %s", gw.Name) | 		r.logger.Infof("Parsing gateway %s", gw.Name) | ||||||
|  | 		if len(gw.Bridges) == 0 { | ||||||
|  | 			return fmt.Errorf("no bridges configured for gateway %s. See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for more info", gw.Name) | ||||||
|  | 		} | ||||||
| 		for _, br := range gw.Bridges { | 		for _, br := range gw.Bridges { | ||||||
| 			m[br.Account] = br | 			m[br.Account] = br | ||||||
| 		} | 		} | ||||||
| @@ -125,6 +131,11 @@ func (r *Router) handleReceive() { | |||||||
| 		r.handleEventGetChannelMembers(&msg) | 		r.handleEventGetChannelMembers(&msg) | ||||||
| 		r.handleEventFailure(&msg) | 		r.handleEventFailure(&msg) | ||||||
| 		r.handleEventRejoinChannels(&msg) | 		r.handleEventRejoinChannels(&msg) | ||||||
|  |  | ||||||
|  | 		// Set message protocol based on the account it came from | ||||||
|  | 		msg.Protocol = r.getBridge(msg.Account).Protocol | ||||||
|  |  | ||||||
|  | 		filesHandled := false | ||||||
| 		for _, gw := range r.Gateways { | 		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 | ||||||
| @@ -133,13 +144,25 @@ func (r *Router) handleReceive() { | |||||||
| 			} | 			} | ||||||
| 			msg.Timestamp = time.Now() | 			msg.Timestamp = time.Now() | ||||||
| 			gw.modifyMessage(&msg) | 			gw.modifyMessage(&msg) | ||||||
| 			gw.handleFiles(&msg) | 			if !filesHandled { | ||||||
|  | 				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) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,71 +3,63 @@ 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.20190816133340-b04c5a83c1c0 | ||||||
| 	github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884 | 	github.com/Rhymen/go-whatsapp v0.1.0 | ||||||
| 	github.com/bwmarrin/discordgo v0.19.0 | 	github.com/d5/tengo/v2 v2.0.2 | ||||||
| 	github.com/d5/tengo v1.9.2 |  | ||||||
| 	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/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 | ||||||
|  | 	github.com/google/gops v0.3.6 | ||||||
| 	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect | 	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect | ||||||
| 	github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect | 	github.com/gorilla/schema v1.1.0 | ||||||
| 	github.com/gorilla/schema v1.0.2 | 	github.com/gorilla/websocket v1.4.1 | ||||||
| 	github.com/gorilla/websocket v1.4.0 | 	github.com/hashicorp/golang-lru v0.5.3 | ||||||
| 	github.com/hashicorp/golang-lru v0.5.0 |  | ||||||
| 	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 v1.0.0 | ||||||
| 	github.com/jtolds/gls v4.2.1+incompatible // indirect | 	github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e | ||||||
| 	github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect | 	github.com/labstack/echo/v4 v4.1.13 | ||||||
| 	github.com/kr/pretty v0.1.0 // indirect | 	github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 | ||||||
| 	github.com/labstack/echo/v4 v4.0.0 |  | ||||||
| 	github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 |  | ||||||
| 	github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // 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/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 | ||||||
|  | 	github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible | ||||||
| 	github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 | 	github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 | ||||||
| 	github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea | 	github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 | ||||||
| 	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/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa | ||||||
| 	github.com/mattermost/mattermost-server v5.5.0+incompatible | 	github.com/mattermost/mattermost-server v5.5.0+incompatible | ||||||
|  | 	github.com/mattn/go-runewidth v0.0.7 // indirect | ||||||
|  | 	github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51 | ||||||
| 	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 | ||||||
| 	github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect | 	github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect | ||||||
| 	github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 | 	github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 | ||||||
| 	github.com/nicksnyder/go-i18n v1.4.0 // indirect | 	github.com/nicksnyder/go-i18n v1.4.0 // indirect | ||||||
| 	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/pkg/errors v0.8.0 // indirect |  | ||||||
| 	github.com/rs/xid v1.2.1 | 	github.com/rs/xid v1.2.1 | ||||||
| 	github.com/russross/blackfriday v2.0.0+incompatible | 	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 v1.8.3-0.20200308224626-80ddf1f43a98 | ||||||
| 	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/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901 | ||||||
| 	github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect | 	github.com/spf13/viper v1.6.1 | ||||||
| 	github.com/spf13/viper v1.3.1 | 	github.com/stretchr/testify v1.4.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/yaegashi/msgraph.go v0.1.2 | ||||||
| 	github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 | 	github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 | ||||||
| 	gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect | 	golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 | ||||||
| 	gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect | 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | ||||||
| 	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/puny v0.0.0-20180912090636-2cd490539afe // indirect |  | ||||||
| 	gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 // indirect |  | ||||||
| 	go.uber.org/atomic v1.3.2 // indirect |  | ||||||
| 	go.uber.org/multierr v1.1.0 // indirect |  | ||||||
| 	go.uber.org/zap v1.9.1 // indirect |  | ||||||
| 	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 | ||||||
| 	maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3 |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | //replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 | ||||||
|  |  | ||||||
|  | go 1.13 | ||||||
|   | |||||||
							
								
								
									
										346
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										346
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,27 +1,40 @@ | |||||||
|  | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||||
|  | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||||
| 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/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY= | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||||
| 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.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ= | ||||||
| github.com/Rhymen/go-whatsapp v0.0.0-20181218094654-2ca6af00572c h1:ldRXgMEfKmzBomrZusl3edG9AGEeztA7jovLEQy62us= | github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg= | ||||||
| github.com/Rhymen/go-whatsapp v0.0.0-20181218094654-2ca6af00572c/go.mod h1:MSDmePOOkbFFbVW2WRRppBcbA+aabwpXRgyIIG7jDFQ= | github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= | ||||||
| github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884 h1:2AxfzkQi2L4QGBvUCZoWD6hQuUJa5MG54wiYyNqJlf4= | github.com/Rhymen/go-whatsapp v0.1.0 h1:XTXhFIQ/fx9jKObUnUX2Q+nh58EyeHNhX7DniE8xeuA= | ||||||
| github.com/Rhymen/go-whatsapp v0.0.0-20190208184307-c9a81e957884/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA= | github.com/Rhymen/go-whatsapp v0.1.0/go.mod h1:xJSy+okeRjKkQEH/lEYrnekXB3PG33fqL0I6ncAkV50= | ||||||
|  | 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 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc= | github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc= | ||||||
| github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE= | github.com/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/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY= | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||||
| github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||||
|  | 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.9.2 h1:UE/X8PYl7bLS4Ww2zGeh91nq5PTnkhe8ncgNeA5PK7k= | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||||||
| github.com/d5/tengo v1.9.2/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY= | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= | ||||||
|  | github.com/d5/tengo/v2 v2.0.2 h1:3APkPZPc1FExaJoWrN5YzvDqc6GNkQH6ehmCRDmN83I= | ||||||
|  | github.com/d5/tengo/v2 v2.0.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= | ||||||
| github.com/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= | ||||||
| @@ -29,74 +42,120 @@ 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/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= | ||||||
|  | github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= | ||||||
|  | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | ||||||
|  | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= | ||||||
|  | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||||
|  | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||||
|  | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||||
| github.com/golang/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/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= | ||||||
| github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0= | 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/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 h1:wJkj+x9gPYlDyM34C6r3SXPs270coWeh85wu1CsusDo= | ||||||
|  | github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= | ||||||
|  | github.com/google/btree 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/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| 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-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= | ||||||
| github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/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/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA= | github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= | ||||||
| github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= | github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||||
| github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= |  | ||||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||||
| github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= | ||||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
|  | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | ||||||
|  | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= | ||||||
|  | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= | ||||||
|  | github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= | ||||||
|  | github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= | ||||||
| github.com/hashicorp/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.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | ||||||
| github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= | ||||||
| github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | ||||||
| github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= | ||||||
| github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | ||||||
|  | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||||
|  | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= | ||||||
| github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= | github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 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-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e h1:KbPTfR/PYuau1IzKoE4lnd8yby5I2pBj+VR6fSVbYU8= | ||||||
|  | github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e/go.mod h1:vNc28YFzigVJod0j5EbuTtRIe7swx8vodh2yA4jZ2s8= | ||||||
|  | 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 v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= | ||||||
| github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno= | github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= | ||||||
| github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0= | github.com/labstack/echo/v4 v4.1.13 h1:JYgKq6NQQSaKbQcsOadAKX1kUVLCUzLGwu8sxN5tC34= | ||||||
| github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= | github.com/labstack/echo/v4 v4.1.13/go.mod h1:3WZNypykZ3tnqpF2Qb4fPg27XDunFqgP3HGDmCMgv7U= | ||||||
| github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 h1:a40kRmhA1p2XFJ6gqXfCExSyuDDCp/U9LA8ZY27u2Lk= | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= | ||||||
| github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= | ||||||
| github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU= | github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 h1:BS9tqL0OCiOGuy/CYYk2gc33fxqaqh5/rhqMKu4tcYA= | ||||||
| github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0= | github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I= | ||||||
| github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 h1:iOAVXzZyXtW408TMYejlUPo6BIn92HmOacWtIfNyYns= | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= | ||||||
| github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg= | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | ||||||
| github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= |  | ||||||
| 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/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw= | ||||||
|  | github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y= | ||||||
|  | github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI= | ||||||
|  | github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4= | ||||||
| github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k= | github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 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-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0= | ||||||
| github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g= | github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g= | ||||||
| github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho= | github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho= | ||||||
| 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/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa h1:ZP87nK5Mhgvt6Rpgbztdmq9+6cb3aZ6MgW/JWKbnMrI= | ||||||
|  | github.com/matterbridge/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa/go.mod h1:l0kx9L8Z+NbBCGrQ/y+ldKZ/fiwBZjPoXwDS55LTumI= | ||||||
| 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 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= | ||||||
| github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | ||||||
| github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= | ||||||
| github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | ||||||
| github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||||
|  | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||||
|  | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= | ||||||
|  | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= | ||||||
|  | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | ||||||
|  | github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= | ||||||
|  | github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||||||
|  | github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51 h1:MpI7hy3MiCnrggmZI/s8LaPbLVOOWpzDbjA4F+XaXaM= | ||||||
|  | github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51/go.mod h1:s3KUdOIXJ+jaGM++XHiXA6gikdleaWVATCcQGD4h734= | ||||||
|  | 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= | ||||||
| @@ -105,50 +164,66 @@ 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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= | ||||||
| github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= |  | ||||||
| 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/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/rickb777/date v1.12.4 h1:+6IzcCCS/1t17DrmnEvrznyq7nM8vPwir6/UhlyohKw= | ||||||
|  | github.com/rickb777/date v1.12.4/go.mod h1:xP0eo/I5qmUt97yRGClHZfyLZ3ikMw6v6SU5MOGZTE0= | ||||||
|  | github.com/rickb777/plural v1.2.0 h1:5tvEc7UBCZ7l8h/2UeybSkt/uu1DQsZFOFdNevmUhlE= | ||||||
|  | github.com/rickb777/plural v1.2.0/go.mod h1:UdpyWFCGbo3mvK3f/PfZOAOrkjzJlYN/sD46XNWJ+Es= | ||||||
|  | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | ||||||
| github.com/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 v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk= | github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= | ||||||
| github.com/russross/blackfriday v2.0.0+incompatible/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 v1.8.3-0.20200308224626-80ddf1f43a98 h1:sN07ff+PSRsUNhpSod4uGKAQ+Nc0FXsBPG9FmYMNg4w= | ||||||
| github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4= | github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98/go.mod h1:xkTgfD+WP+KR4HuG76oal25BBEeu5kJyi2EOsgiu/4Q= | ||||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= | github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= | ||||||
| github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME= | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | ||||||
| github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= | ||||||
| github.com/skip2/go-qrcode v0.0.0-20171229120447-cf5f9fa2f0d8/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= | 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/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901 h1:sXIMY2YPYEm5NoGMCrJC50N+8t9W6vbY9qr61zcLEAE= | ||||||
| github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | github.com/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901/go.mod h1:ZUNi+O1Pwr2ch2UOp2AfF+s7QYQgwht2Cd1UTeIYw9A= | ||||||
| github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | ||||||
| github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||||
|  | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= | ||||||
|  | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= | ||||||
|  | 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,81 +232,118 @@ 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.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= | ||||||
| github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= | github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= | ||||||
| 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 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= |  | ||||||
| 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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= |  | ||||||
| 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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||||
|  | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= | ||||||
|  | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= | ||||||
| github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= | github.com/technoweenie/multipartstreamer v1.0.1 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/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= | ||||||
| github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw= | github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= | ||||||
|  | github.com/valyala/fasttemplate v1.1.0/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/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/yaegashi/msgraph.go v0.1.2 h1:83uVRQaj8YBsVqOUGj0WRwzxdgGF69jRpg5IQYaTvoY= | ||||||
|  | github.com/yaegashi/msgraph.go v0.1.2/go.mod h1:Lp39e9oo596G5FcmMKI0cXR3mg/QikSdabgZdbMqbAM= | ||||||
| github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo= | github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo= | ||||||
| github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= | github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= | ||||||
| gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0= | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | ||||||
| gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI= | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= | ||||||
| gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk= | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= | ||||||
| gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8= |  | ||||||
| gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f h1:jwXy/CsM4xS2aoiF2fHAlukmInWhd2TlWB+HDCyvzKc= |  | ||||||
| gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f/go.mod h1:SIHlEr9462fpIfTrVWf3GqQDxnA65Vm3BMMsUtuA6W0= |  | ||||||
| gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 h1:wD/sPUgx2QJFPTyXZpJnLaROolfeKuruh06U4pRV0WY= |  | ||||||
| gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2/go.mod h1:wQk4rLkWrdOPjUAtqJRJ10hIlseLSVYWP95PLrjDF9s= |  | ||||||
| gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe h1:5kUPFAF52umOUPH12MuNUmyVTseJRNBftDl/KfsvX3I= |  | ||||||
| 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/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU= |  | ||||||
| go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= |  | ||||||
| go.uber.org/atomic v1.3.2/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/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= | ||||||
| golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||||
| golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= |  | ||||||
| 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-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |  | ||||||
| golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8= |  | ||||||
| golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= |  | ||||||
| golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc= |  | ||||||
| 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/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
|  | golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= | ||||||
|  | golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
|  | golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0= | ||||||
|  | golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||||
|  | golang.org/x/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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
|  | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
|  | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
|  | golang.org/x/net v0.0.0-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-20190108225652-1e06a53dbb7e/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-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= | ||||||
|  | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
|  | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= |  | ||||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20181212120007-b05ddf57801d/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-20190109145017-48ac38b7c8cb/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-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ= | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM= | ||||||
|  | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/text v0.3.0/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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||||
|  | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||||
|  | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | ||||||
|  | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||||
|  | google.golang.org/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= | ||||||
| gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | 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/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= | ||||||
|  | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||||
| 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.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | 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/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= | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= | ||||||
| maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI= | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
| maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI= | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM= | rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= | ||||||
| maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3 h1:A18t5Lp7I3aK0V7B7zdpb0hb/PBlu0X/Ai2AyU/XEk4= |  | ||||||
| maunium.net/go/mautrix-whatsapp v0.0.0-20190127121751-281b3e8f77f3/go.mod h1:r5E3J4urDEsjfui9OYZYMLBfCliaAqcCwM2xeczta6k= |  | ||||||
|   | |||||||
							
								
								
									
										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, "/")...)...) | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								internal/tengo/outmessage.tengo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								internal/tengo/outmessage.tengo
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | /* | ||||||
|  | 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" && outProtocol != "irc" { | ||||||
|  |     re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`) | ||||||
|  |     msgText=re.replace(msgText,"") | ||||||
|  | } | ||||||
|  | // end - strip irc colors | ||||||
|  |  | ||||||
|  | // strip custom emoji | ||||||
|  | if inProtocol == "discord" { | ||||||
|  |     re := text.re_compile(`<a?(:.*?:)[0-9]+>`) | ||||||
|  |     msgText=re.replace(msgText,"$1") | ||||||
|  | } | ||||||
| @@ -15,7 +15,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	version = "1.14.0-rc1" | 	version = "1.17.1" | ||||||
| 	githash string | 	githash string | ||||||
|  |  | ||||||
| 	flagConfig  = flag.String("conf", "matterbridge.toml", "config file") | 	flagConfig  = flag.String("conf", "matterbridge.toml", "config file") | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -36,6 +36,16 @@ func (m *MMClient) GetChannelHeader(channelId string) string { //nolint:golint | |||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func getNormalisedName(channel *model.Channel) string { | ||||||
|  | 	if channel.Type == model.CHANNEL_GROUP { | ||||||
|  | 		// (deprecated in favor of ReplaceAll in go 1.12) | ||||||
|  | 		res := strings.Replace(channel.DisplayName, ", ", "-", -1) //nolint: gocritic | ||||||
|  | 		res = strings.Replace(res, " ", "_", -1)                   //nolint: gocritic | ||||||
|  | 		return res | ||||||
|  | 	} | ||||||
|  | 	return channel.Name | ||||||
|  | } | ||||||
|  |  | ||||||
| func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:golint | func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:golint | ||||||
| 	m.RLock() | 	m.RLock() | ||||||
| 	defer m.RUnlock() | 	defer m.RUnlock() | ||||||
| @@ -45,14 +55,9 @@ func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:go | |||||||
|  |  | ||||||
| 	for _, t := range m.OtherTeams { | 	for _, t := range m.OtherTeams { | ||||||
| 		for _, channel := range append(t.Channels, t.MoreChannels...) { | 		for _, channel := range append(t.Channels, t.MoreChannels...) { | ||||||
| 			if channel.Type == model.CHANNEL_GROUP { | 			if getNormalisedName(channel) == name { | ||||||
| 				res := strings.Replace(channel.DisplayName, ", ", "-", -1) | 				return channel.Id | ||||||
| 				res = strings.Replace(res, " ", "_", -1) |  | ||||||
| 				if res == name { |  | ||||||
| 					return channel.Id |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| @@ -62,7 +67,7 @@ func (m *MMClient) getChannelIdTeam(name string, teamId string) string { //nolin | |||||||
| 	for _, t := range m.OtherTeams { | 	for _, t := range m.OtherTeams { | ||||||
| 		if t.Id == teamId { | 		if t.Id == teamId { | ||||||
| 			for _, channel := range append(t.Channels, t.MoreChannels...) { | 			for _, channel := range append(t.Channels, t.MoreChannels...) { | ||||||
| 				if channel.Name == name { | 				if getNormalisedName(channel) == name { | ||||||
| 					return channel.Id | 					return channel.Id | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -80,12 +85,7 @@ func (m *MMClient) GetChannelName(channelId string) string { //nolint:golint | |||||||
| 		} | 		} | ||||||
| 		for _, channel := range append(t.Channels, t.MoreChannels...) { | 		for _, channel := range append(t.Channels, t.MoreChannels...) { | ||||||
| 			if channel.Id == channelId { | 			if channel.Id == channelId { | ||||||
| 				if channel.Type == model.CHANNEL_GROUP { | 				return getNormalisedName(channel) | ||||||
| 					res := strings.Replace(channel.DisplayName, ", ", "-", -1) |  | ||||||
| 					res = strings.Replace(res, " ", "_", -1) |  | ||||||
| 					return res |  | ||||||
| 				} |  | ||||||
| 				return channel.Name |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -166,23 +166,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 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/gorilla/schema" | 	"github.com/gorilla/schema" | ||||||
| 	"github.com/nlopes/slack" | 	"github.com/slack-go/slack" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // OMessage for mattermost incoming webhook. (send to mattermost) | // OMessage for mattermost incoming webhook. (send to mattermost) | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								vendor/github.com/Philipp15b/go-steam/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/Philipp15b/go-steam/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -45,20 +45,11 @@ Whether you want to develop your own Steam bot or directly work on go-steam itse | |||||||
|  |  | ||||||
| ## Updating go-steam to a new SteamKit version | ## Updating go-steam to a new SteamKit version | ||||||
|  |  | ||||||
| To update go-steam to a new version of SteamKit, do the following: | Go source code is generated with code in the `generator` directory. | ||||||
|  | Look at `generator/README.md` for more information on how to use the generator. | ||||||
|  |  | ||||||
| 	go get github.com/golang/protobuf/protoc-gen-go/ | Then, after generating new Go source files, update `go-steam` as necessary. | ||||||
|     git submodule init && git submodule update |  | ||||||
|     cd generator |  | ||||||
|     go run generator.go clean proto steamlang |  | ||||||
|  |  | ||||||
| Make sure that `$GOPATH/bin` / `protoc-gen-go` is in your `$PATH`. You'll also need [`protoc`](https://developers.google.com/protocol-buffers/docs/downloads), the protocol buffer compiler. At the moment, we use Protocol Buffers 2.6.1 with `proco-gen-go`-[2402d76](https://github.com/golang/protobuf/tree/2402d76f3d41f928c7902a765dfc872356dd3aad). |  | ||||||
|  |  | ||||||
| To compile the Steam Language files, you also need the [.NET Framework](https://www.microsoft.com/net/downloads) |  | ||||||
| on Windows or [mono](http://www.go-mono.com/mono-downloads/download.html) on other operating systems. |  | ||||||
|  |  | ||||||
| Apply the protocol changes where necessary. |  | ||||||
|  |  | ||||||
| ## License | ## License | ||||||
|  |  | ||||||
| Steam for Go is licensed under the New BSD License. More information can be found in LICENSE.txt. | Steam for Go is licensed under the New BSD License. More information can be found in LICENSE.txt. | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								vendor/github.com/Philipp15b/go-steam/auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Philipp15b/go-steam/auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -94,9 +94,6 @@ func (a *Auth) HandlePacket(packet *Packet) { | |||||||
| 		a.handleUpdateMachineAuth(packet) | 		a.handleUpdateMachineAuth(packet) | ||||||
| 	case EMsg_ClientAccountInfo: | 	case EMsg_ClientAccountInfo: | ||||||
| 		a.handleAccountInfo(packet) | 		a.handleAccountInfo(packet) | ||||||
| 	case EMsg_ClientWalletInfoUpdate: |  | ||||||
| 	case EMsg_ClientRequestWebAPIAuthenticateUserNonceResponse: |  | ||||||
| 	case EMsg_ClientMarketingMessageUpdate: |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								vendor/github.com/Philipp15b/go-steam/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/Philipp15b/go-steam/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -133,11 +133,17 @@ func (c *Client) Connected() bool { | |||||||
| // If you want to connect to a specific server, use `ConnectTo`. | // If you want to connect to a specific server, use `ConnectTo`. | ||||||
| func (c *Client) Connect() *netutil.PortAddr { | func (c *Client) Connect() *netutil.PortAddr { | ||||||
| 	var server *netutil.PortAddr | 	var server *netutil.PortAddr | ||||||
|  |  | ||||||
|  | 	// try to initialize the directory cache | ||||||
|  | 	if !steamDirectoryCache.IsInitialized() { | ||||||
|  | 		_ = steamDirectoryCache.Initialize() | ||||||
|  | 	} | ||||||
| 	if steamDirectoryCache.IsInitialized() { | 	if steamDirectoryCache.IsInitialized() { | ||||||
| 		server = steamDirectoryCache.GetRandomCM() | 		server = steamDirectoryCache.GetRandomCM() | ||||||
| 	} else { | 	} else { | ||||||
| 		server = GetRandomCM() | 		server = GetRandomCM() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c.ConnectTo(server) | 	c.ConnectTo(server) | ||||||
| 	return server | 	return server | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/internal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/internal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| package protocol | package protocol | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/hex" | ||||||
| 	"io" | 	"io" | ||||||
| 	"math" | 	"math" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| @@ -42,6 +43,7 @@ const EClientPersonaStateFlag_DefaultInfoRequest = EClientPersonaStateFlag_Playe | |||||||
|  |  | ||||||
| const DefaultAvatar = "fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb" | const DefaultAvatar = "fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb" | ||||||
|  |  | ||||||
| func ValidAvatar(avatar string) bool { | func ValidAvatar(avatar []byte) bool { | ||||||
| 	return !(avatar == "0000000000000000000000000000000000000000" || len(avatar) != 40) | 	str := hex.EncodeToString(avatar) | ||||||
|  | 	return !(str == "0000000000000000000000000000000000000000" || len(str) != 40) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										75
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/app_ticket.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/app_ticket.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,31 +1,60 @@ | |||||||
| // Code generated by protoc-gen-go. | // Code generated by protoc-gen-go. DO NOT EDIT. | ||||||
| // source: encrypted_app_ticket.proto | // source: encrypted_app_ticket.proto | ||||||
| // DO NOT EDIT! |  | ||||||
|  |  | ||||||
| package protobuf | package protobuf | ||||||
|  |  | ||||||
| import proto "github.com/golang/protobuf/proto" | import ( | ||||||
| import fmt "fmt" | 	fmt "fmt" | ||||||
| import math "math" | 	proto "github.com/golang/protobuf/proto" | ||||||
|  | 	math "math" | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. | // Reference imports to suppress errors if they are not otherwise used. | ||||||
| var _ = proto.Marshal | var _ = proto.Marshal | ||||||
| var _ = fmt.Errorf | var _ = fmt.Errorf | ||||||
| var _ = math.Inf | var _ = math.Inf | ||||||
|  |  | ||||||
|  | // This is a compile-time assertion to ensure that this generated file | ||||||
|  | // is compatible with the proto package protobuf is being compiled against. | ||||||
|  | // A compilation error at this line likely means your copy of the | ||||||
|  | // proto package protobuf to be updated. | ||||||
|  | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||||
|  |  | ||||||
| type EncryptedAppTicket struct { | type EncryptedAppTicket struct { | ||||||
| 	TicketVersionNo               *uint32 `protobuf:"varint,1,opt,name=ticket_version_no" json:"ticket_version_no,omitempty"` | 	TicketVersionNo               *uint32  `protobuf:"varint,1,opt,name=ticket_version_no" json:"ticket_version_no,omitempty"` | ||||||
| 	CrcEncryptedticket            *uint32 `protobuf:"varint,2,opt,name=crc_encryptedticket" json:"crc_encryptedticket,omitempty"` | 	CrcEncryptedticket            *uint32  `protobuf:"varint,2,opt,name=crc_encryptedticket" json:"crc_encryptedticket,omitempty"` | ||||||
| 	CbEncrypteduserdata           *uint32 `protobuf:"varint,3,opt,name=cb_encrypteduserdata" json:"cb_encrypteduserdata,omitempty"` | 	CbEncrypteduserdata           *uint32  `protobuf:"varint,3,opt,name=cb_encrypteduserdata" json:"cb_encrypteduserdata,omitempty"` | ||||||
| 	CbEncryptedAppownershipticket *uint32 `protobuf:"varint,4,opt,name=cb_encrypted_appownershipticket" json:"cb_encrypted_appownershipticket,omitempty"` | 	CbEncryptedAppownershipticket *uint32  `protobuf:"varint,4,opt,name=cb_encrypted_appownershipticket" json:"cb_encrypted_appownershipticket,omitempty"` | ||||||
| 	EncryptedTicket               []byte  `protobuf:"bytes,5,opt,name=encrypted_ticket" json:"encrypted_ticket,omitempty"` | 	EncryptedTicket               []byte   `protobuf:"bytes,5,opt,name=encrypted_ticket" json:"encrypted_ticket,omitempty"` | ||||||
| 	XXX_unrecognized              []byte  `json:"-"` | 	XXX_NoUnkeyedLiteral          struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized              []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache                 int32    `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *EncryptedAppTicket) Reset()                    { *m = EncryptedAppTicket{} } | func (m *EncryptedAppTicket) Reset()         { *m = EncryptedAppTicket{} } | ||||||
| func (m *EncryptedAppTicket) String() string            { return proto.CompactTextString(m) } | func (m *EncryptedAppTicket) String() string { return proto.CompactTextString(m) } | ||||||
| func (*EncryptedAppTicket) ProtoMessage()               {} | func (*EncryptedAppTicket) ProtoMessage()    {} | ||||||
| func (*EncryptedAppTicket) Descriptor() ([]byte, []int) { return app_ticket_fileDescriptor0, []int{0} } | func (*EncryptedAppTicket) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_c6d69fd1cac4e8d5, []int{0} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *EncryptedAppTicket) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_EncryptedAppTicket.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *EncryptedAppTicket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_EncryptedAppTicket.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *EncryptedAppTicket) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_EncryptedAppTicket.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *EncryptedAppTicket) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_EncryptedAppTicket.Size(m) | ||||||
|  | } | ||||||
|  | func (m *EncryptedAppTicket) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_EncryptedAppTicket.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_EncryptedAppTicket proto.InternalMessageInfo | ||||||
|  |  | ||||||
| func (m *EncryptedAppTicket) GetTicketVersionNo() uint32 { | func (m *EncryptedAppTicket) GetTicketVersionNo() uint32 { | ||||||
| 	if m != nil && m.TicketVersionNo != nil { | 	if m != nil && m.TicketVersionNo != nil { | ||||||
| @@ -66,17 +95,19 @@ func init() { | |||||||
| 	proto.RegisterType((*EncryptedAppTicket)(nil), "EncryptedAppTicket") | 	proto.RegisterType((*EncryptedAppTicket)(nil), "EncryptedAppTicket") | ||||||
| } | } | ||||||
|  |  | ||||||
| var app_ticket_fileDescriptor0 = []byte{ | func init() { proto.RegisterFile("encrypted_app_ticket.proto", fileDescriptor_c6d69fd1cac4e8d5) } | ||||||
| 	// 162 bytes of a gzipped FileDescriptorProto |  | ||||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcd, 0x4b, 0x2e, | var fileDescriptor_c6d69fd1cac4e8d5 = []byte{ | ||||||
|  | 	// 164 bytes of a gzipped FileDescriptorProto | ||||||
|  | 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcd, 0x4b, 0x2e, | ||||||
| 	0xaa, 0x2c, 0x28, 0x49, 0x4d, 0x89, 0x4f, 0x2c, 0x28, 0x88, 0x2f, 0xc9, 0x4c, 0xce, 0x4e, 0x2d, | 	0xaa, 0x2c, 0x28, 0x49, 0x4d, 0x89, 0x4f, 0x2c, 0x28, 0x88, 0x2f, 0xc9, 0x4c, 0xce, 0x4e, 0x2d, | ||||||
| 	0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x5a, 0xcb, 0xc8, 0x25, 0xe4, 0x0a, 0x93, 0x76, 0x2c, | 	0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x5a, 0xcb, 0xc8, 0x25, 0xe4, 0x0a, 0x93, 0x76, 0x2c, | ||||||
| 	0x28, 0x08, 0x01, 0x4b, 0x0a, 0x49, 0x72, 0x09, 0x42, 0x94, 0xc5, 0x97, 0xa5, 0x16, 0x15, 0x67, | 	0x28, 0x08, 0x01, 0x4b, 0x0a, 0x49, 0x72, 0x09, 0x42, 0x94, 0xc5, 0x97, 0xa5, 0x16, 0x15, 0x67, | ||||||
| 	0xe6, 0xe7, 0xc5, 0xe7, 0xe5, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x0a, 0x49, 0x73, 0x09, 0x27, | 	0xe6, 0xe7, 0xc5, 0xe7, 0xe5, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x0a, 0x49, 0x73, 0x09, 0x27, | ||||||
| 	0x17, 0x25, 0xc7, 0xc3, 0xcd, 0x84, 0xa8, 0x93, 0x60, 0x02, 0x4b, 0xca, 0x70, 0x89, 0x24, 0x27, | 	0x17, 0x25, 0xc7, 0xc3, 0xcd, 0x84, 0xa8, 0x93, 0x60, 0x02, 0x4b, 0xca, 0x70, 0x89, 0x24, 0x27, | ||||||
| 	0x21, 0xe4, 0x4a, 0x8b, 0x53, 0x8b, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1, 0xb2, 0xea, 0x5c, | 	0x21, 0xe4, 0x4a, 0x8b, 0x53, 0x8b, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1, 0xb2, 0xea, 0x5c, | ||||||
| 	0xf2, 0xc8, 0xb2, 0x20, 0xd7, 0xe4, 0x97, 0xe7, 0x01, 0x2d, 0xc8, 0xc8, 0x2c, 0x80, 0x1a, 0xc3, | 	0xf2, 0xc8, 0xb2, 0x20, 0xd7, 0xe4, 0x97, 0xe7, 0xa5, 0x16, 0x15, 0x67, 0x64, 0x16, 0x40, 0x8d, | ||||||
| 	0x02, 0x56, 0x28, 0xc1, 0x25, 0x80, 0x50, 0x05, 0x95, 0x61, 0x05, 0xca, 0xf0, 0x38, 0xb1, 0x7a, | 	0x61, 0x01, 0x2b, 0x94, 0xe0, 0x12, 0x40, 0xa8, 0x82, 0xca, 0xb0, 0x2a, 0x30, 0x6a, 0xf0, 0x38, | ||||||
| 	0x30, 0x36, 0x30, 0x32, 0x00, 0x02, 0x00, 0x00, 0xff, 0xff, 0x03, 0x8c, 0xdb, 0x92, 0xd3, 0x00, | 	0xb1, 0x7a, 0x30, 0x36, 0x30, 0x32, 0x00, 0x02, 0x00, 0x00, 0xff, 0xff, 0x03, 0x8c, 0xdb, 0x92, | ||||||
| 	0x00, 0x00, | 	0xd3, 0x00, 0x00, 0x00, | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										1036
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/base.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1036
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/base.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										9031
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9031
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										7738
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_2.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7738
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_2.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2321
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_friends.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2321
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_friends.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1294
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_login.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1294
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_login.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										397
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_site_license.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_site_license.pb.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,397 @@ | |||||||
|  | // Code generated by protoc-gen-go. DO NOT EDIT. | ||||||
|  | // source: steammessages_sitelicenseclient.proto | ||||||
|  |  | ||||||
|  | package protobuf | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	fmt "fmt" | ||||||
|  | 	proto "github.com/golang/protobuf/proto" | ||||||
|  | 	math "math" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Reference imports to suppress errors if they are not otherwise used. | ||||||
|  | var _ = proto.Marshal | ||||||
|  | var _ = fmt.Errorf | ||||||
|  | var _ = math.Inf | ||||||
|  |  | ||||||
|  | // This is a compile-time assertion to ensure that this generated file | ||||||
|  | // is compatible with the proto package protobuf is being compiled against. | ||||||
|  | // A compilation error at this line likely means your copy of the | ||||||
|  | // proto package protobuf to be updated. | ||||||
|  | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||||
|  |  | ||||||
|  | type CMsgClientSiteInfo struct { | ||||||
|  | 	SiteId                 *uint64  `protobuf:"varint,1,opt,name=site_id" json:"site_id,omitempty"` | ||||||
|  | 	SiteName               *string  `protobuf:"bytes,2,opt,name=site_name" json:"site_name,omitempty"` | ||||||
|  | 	AllowCachedCredentials *bool    `protobuf:"varint,3,opt,name=allow_cached_credentials" json:"allow_cached_credentials,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral   struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized       []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache          int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteInfo) Reset()         { *m = CMsgClientSiteInfo{} } | ||||||
|  | func (m *CMsgClientSiteInfo) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*CMsgClientSiteInfo) ProtoMessage()    {} | ||||||
|  | func (*CMsgClientSiteInfo) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{0} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteInfo) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteInfo.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteInfo.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteInfo) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteInfo.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteInfo) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteInfo.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteInfo) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteInfo.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteInfo proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteInfo) GetSiteId() uint64 { | ||||||
|  | 	if m != nil && m.SiteId != nil { | ||||||
|  | 		return *m.SiteId | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteInfo) GetSiteName() string { | ||||||
|  | 	if m != nil && m.SiteName != nil { | ||||||
|  | 		return *m.SiteName | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteInfo) GetAllowCachedCredentials() bool { | ||||||
|  | 	if m != nil && m.AllowCachedCredentials != nil { | ||||||
|  | 		return *m.AllowCachedCredentials | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CMsgClientSiteLicenseCheckout struct { | ||||||
|  | 	Appid                *uint32  `protobuf:"varint,1,opt,name=appid" json:"appid,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) Reset()         { *m = CMsgClientSiteLicenseCheckout{} } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*CMsgClientSiteLicenseCheckout) ProtoMessage()    {} | ||||||
|  | func (*CMsgClientSiteLicenseCheckout) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{1} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseCheckout.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseCheckout.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseCheckout.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseCheckout.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseCheckout.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteLicenseCheckout proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseCheckout) GetAppid() uint32 { | ||||||
|  | 	if m != nil && m.Appid != nil { | ||||||
|  | 		return *m.Appid | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CMsgClientSiteLicenseCheckoutResponse struct { | ||||||
|  | 	Eresult              *int32   `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) Reset()         { *m = CMsgClientSiteLicenseCheckoutResponse{} } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*CMsgClientSiteLicenseCheckoutResponse) ProtoMessage()    {} | ||||||
|  | func (*CMsgClientSiteLicenseCheckoutResponse) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{2} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | const Default_CMsgClientSiteLicenseCheckoutResponse_Eresult int32 = 2 | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseCheckoutResponse) GetEresult() int32 { | ||||||
|  | 	if m != nil && m.Eresult != nil { | ||||||
|  | 		return *m.Eresult | ||||||
|  | 	} | ||||||
|  | 	return Default_CMsgClientSiteLicenseCheckoutResponse_Eresult | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CMsgClientSiteLicenseGetAvailableSeats struct { | ||||||
|  | 	Appid                *uint32  `protobuf:"varint,1,opt,name=appid" json:"appid,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) Reset() { | ||||||
|  | 	*m = CMsgClientSiteLicenseGetAvailableSeats{} | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*CMsgClientSiteLicenseGetAvailableSeats) ProtoMessage()    {} | ||||||
|  | func (*CMsgClientSiteLicenseGetAvailableSeats) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{3} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeats) GetAppid() uint32 { | ||||||
|  | 	if m != nil && m.Appid != nil { | ||||||
|  | 		return *m.Appid | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CMsgClientSiteLicenseGetAvailableSeatsResponse struct { | ||||||
|  | 	Eresult              *int32   `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||||
|  | 	Seats                *uint32  `protobuf:"varint,2,opt,name=seats" json:"seats,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) Reset() { | ||||||
|  | 	*m = CMsgClientSiteLicenseGetAvailableSeatsResponse{} | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) String() string { | ||||||
|  | 	return proto.CompactTextString(m) | ||||||
|  | } | ||||||
|  | func (*CMsgClientSiteLicenseGetAvailableSeatsResponse) ProtoMessage() {} | ||||||
|  | func (*CMsgClientSiteLicenseGetAvailableSeatsResponse) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{4} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | const Default_CMsgClientSiteLicenseGetAvailableSeatsResponse_Eresult int32 = 2 | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) GetEresult() int32 { | ||||||
|  | 	if m != nil && m.Eresult != nil { | ||||||
|  | 		return *m.Eresult | ||||||
|  | 	} | ||||||
|  | 	return Default_CMsgClientSiteLicenseGetAvailableSeatsResponse_Eresult | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) GetSeats() uint32 { | ||||||
|  | 	if m != nil && m.Seats != nil { | ||||||
|  | 		return *m.Seats | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CMsgClientSiteLicenseGetContentCacheInfo struct { | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) Reset() { | ||||||
|  | 	*m = CMsgClientSiteLicenseGetContentCacheInfo{} | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*CMsgClientSiteLicenseGetContentCacheInfo) ProtoMessage()    {} | ||||||
|  | func (*CMsgClientSiteLicenseGetContentCacheInfo) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{5} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | type CMsgClientSiteLicenseGetContentCacheInfoResponse struct { | ||||||
|  | 	UseCache             *bool    `protobuf:"varint,1,opt,name=use_cache" json:"use_cache,omitempty"` | ||||||
|  | 	Ipv4Address          *uint32  `protobuf:"varint,2,opt,name=ipv4_address" json:"ipv4_address,omitempty"` | ||||||
|  | 	PortNumber           *uint32  `protobuf:"varint,3,opt,name=port_number" json:"port_number,omitempty"` | ||||||
|  | 	P2PGroup             *uint32  `protobuf:"varint,4,opt,name=p2p_group" json:"p2p_group,omitempty"` | ||||||
|  | 	IpAddress            *string  `protobuf:"bytes,5,opt,name=ip_address" json:"ip_address,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) Reset() { | ||||||
|  | 	*m = CMsgClientSiteLicenseGetContentCacheInfoResponse{} | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) String() string { | ||||||
|  | 	return proto.CompactTextString(m) | ||||||
|  | } | ||||||
|  | func (*CMsgClientSiteLicenseGetContentCacheInfoResponse) ProtoMessage() {} | ||||||
|  | func (*CMsgClientSiteLicenseGetContentCacheInfoResponse) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_0a32817a56a37a6e, []int{6} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Size(m) | ||||||
|  | } | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetUseCache() bool { | ||||||
|  | 	if m != nil && m.UseCache != nil { | ||||||
|  | 		return *m.UseCache | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetIpv4Address() uint32 { | ||||||
|  | 	if m != nil && m.Ipv4Address != nil { | ||||||
|  | 		return *m.Ipv4Address | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetPortNumber() uint32 { | ||||||
|  | 	if m != nil && m.PortNumber != nil { | ||||||
|  | 		return *m.PortNumber | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetP2PGroup() uint32 { | ||||||
|  | 	if m != nil && m.P2PGroup != nil { | ||||||
|  | 		return *m.P2PGroup | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetIpAddress() string { | ||||||
|  | 	if m != nil && m.IpAddress != nil { | ||||||
|  | 		return *m.IpAddress | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteInfo)(nil), "CMsgClientSiteInfo") | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteLicenseCheckout)(nil), "CMsgClientSiteLicenseCheckout") | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteLicenseCheckoutResponse)(nil), "CMsgClientSiteLicenseCheckoutResponse") | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteLicenseGetAvailableSeats)(nil), "CMsgClientSiteLicenseGetAvailableSeats") | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteLicenseGetAvailableSeatsResponse)(nil), "CMsgClientSiteLicenseGetAvailableSeatsResponse") | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteLicenseGetContentCacheInfo)(nil), "CMsgClientSiteLicenseGetContentCacheInfo") | ||||||
|  | 	proto.RegisterType((*CMsgClientSiteLicenseGetContentCacheInfoResponse)(nil), "CMsgClientSiteLicenseGetContentCacheInfoResponse") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	proto.RegisterFile("steammessages_sitelicenseclient.proto", fileDescriptor_0a32817a56a37a6e) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var fileDescriptor_0a32817a56a37a6e = []byte{ | ||||||
|  | 	// 335 bytes of a gzipped FileDescriptorProto | ||||||
|  | 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x41, 0x4b, 0xfb, 0x40, | ||||||
|  | 	0x14, 0xc4, 0xff, 0xfb, 0xb7, 0xa1, 0xed, 0xd3, 0x20, 0x5d, 0x3d, 0x04, 0x41, 0x08, 0x81, 0x4a, | ||||||
|  | 	0xf0, 0x50, 0xa4, 0x08, 0x82, 0x9e, 0x34, 0x07, 0x15, 0xf4, 0x62, 0x6f, 0x5e, 0xc2, 0x36, 0x79, | ||||||
|  | 	0xb6, 0x8b, 0x9b, 0xdd, 0x25, 0x6f, 0x53, 0xaf, 0x7e, 0x0b, 0xbf, 0xae, 0x64, 0x5b, 0x0a, 0x15, | ||||||
|  | 	0x2d, 0xbd, 0xee, 0xce, 0x6f, 0x66, 0x98, 0x07, 0x43, 0x72, 0x28, 0xaa, 0x0a, 0x89, 0xc4, 0x0c, | ||||||
|  | 	0x29, 0x27, 0xe9, 0x50, 0xc9, 0x02, 0x35, 0x61, 0xa1, 0x24, 0x6a, 0x37, 0xb2, 0xb5, 0x71, 0xe6, | ||||||
|  | 	0x24, 0xda, 0x94, 0x4d, 0x05, 0xe1, 0xf2, 0x27, 0x79, 0x05, 0x9e, 0x3d, 0xd3, 0x2c, 0xf3, 0xea, | ||||||
|  | 	0x89, 0x74, 0xf8, 0xa8, 0xdf, 0x0c, 0x3f, 0x84, 0x6e, 0x6b, 0x95, 0xcb, 0x32, 0x62, 0x31, 0x4b, | ||||||
|  | 	0x3b, 0x7c, 0x00, 0x7d, 0xff, 0xa0, 0x45, 0x85, 0xd1, 0xff, 0x98, 0xa5, 0x7d, 0x1e, 0x43, 0x24, | ||||||
|  | 	0x94, 0x32, 0x1f, 0x79, 0x21, 0x8a, 0x39, 0x96, 0x79, 0x51, 0x63, 0x89, 0xda, 0x49, 0xa1, 0x28, | ||||||
|  | 	0xda, 0x8b, 0x59, 0xda, 0x4b, 0x46, 0x70, 0xba, 0xe9, 0xfd, 0xb4, 0xac, 0x96, 0xcd, 0xb1, 0x78, | ||||||
|  | 	0x37, 0x8d, 0xe3, 0x21, 0x04, 0xc2, 0xda, 0x55, 0x48, 0x98, 0xdc, 0xc0, 0x70, 0xab, 0xfe, 0x05, | ||||||
|  | 	0xc9, 0x1a, 0x4d, 0xc8, 0x39, 0x74, 0xb1, 0x46, 0x6a, 0x94, 0xf3, 0x64, 0x70, 0xcd, 0xc6, 0xc9, | ||||||
|  | 	0x15, 0x9c, 0xfd, 0x0a, 0xdf, 0xa3, 0xbb, 0x5d, 0x08, 0xa9, 0xc4, 0x54, 0xe1, 0x04, 0x85, 0xa3, | ||||||
|  | 	0x9f, 0xa9, 0x13, 0x18, 0xed, 0x06, 0x6e, 0x8b, 0x6f, 0x4d, 0xa9, 0x15, 0xf9, 0x71, 0xc2, 0xe4, | ||||||
|  | 	0x1c, 0xd2, 0xbf, 0x4c, 0x33, 0xa3, 0x1d, 0x6a, 0x97, 0xb5, 0xab, 0xb5, 0x63, 0x27, 0x5f, 0x0c, | ||||||
|  | 	0x2e, 0x76, 0x15, 0xaf, 0x3b, 0x0c, 0xa0, 0xdf, 0x10, 0x2e, 0xb7, 0xf7, 0x2d, 0x7a, 0xfc, 0x18, | ||||||
|  | 	0x0e, 0xa4, 0x5d, 0x5c, 0xe6, 0xa2, 0x2c, 0x6b, 0xa4, 0x55, 0x13, 0x7e, 0x04, 0xfb, 0xd6, 0xd4, | ||||||
|  | 	0x2e, 0xd7, 0x4d, 0x35, 0xc5, 0xda, 0x5f, 0x26, 0x6c, 0x69, 0x3b, 0xb6, 0xf9, 0xac, 0x36, 0x8d, | ||||||
|  | 	0x8d, 0x3a, 0xfe, 0x89, 0x03, 0x48, 0xbb, 0x66, 0x83, 0xf6, 0xc4, 0x77, 0xc1, 0x03, 0xfb, 0x64, | ||||||
|  | 	0xff, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0x09, 0x2f, 0x9f, 0xe9, 0x65, 0x02, 0x00, 0x00, | ||||||
|  | } | ||||||
							
								
								
									
										407
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/content_manifest.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										407
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/content_manifest.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,27 +1,56 @@ | |||||||
| // Code generated by protoc-gen-go. | // Code generated by protoc-gen-go. DO NOT EDIT. | ||||||
| // source: content_manifest.proto | // source: content_manifest.proto | ||||||
| // DO NOT EDIT! |  | ||||||
|  |  | ||||||
| package protobuf | package protobuf | ||||||
|  |  | ||||||
| import proto "github.com/golang/protobuf/proto" | import ( | ||||||
| import fmt "fmt" | 	fmt "fmt" | ||||||
| import math "math" | 	proto "github.com/golang/protobuf/proto" | ||||||
|  | 	math "math" | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. | // Reference imports to suppress errors if they are not otherwise used. | ||||||
| var _ = proto.Marshal | var _ = proto.Marshal | ||||||
| var _ = fmt.Errorf | var _ = fmt.Errorf | ||||||
| var _ = math.Inf | var _ = math.Inf | ||||||
|  |  | ||||||
|  | // This is a compile-time assertion to ensure that this generated file | ||||||
|  | // is compatible with the proto package protobuf is being compiled against. | ||||||
|  | // A compilation error at this line likely means your copy of the | ||||||
|  | // proto package protobuf to be updated. | ||||||
|  | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||||
|  |  | ||||||
| type ContentManifestPayload struct { | type ContentManifestPayload struct { | ||||||
| 	Mappings         []*ContentManifestPayload_FileMapping `protobuf:"bytes,1,rep,name=mappings" json:"mappings,omitempty"` | 	Mappings             []*ContentManifestPayload_FileMapping `protobuf:"bytes,1,rep,name=mappings" json:"mappings,omitempty"` | ||||||
| 	XXX_unrecognized []byte                                `json:"-"` | 	XXX_NoUnkeyedLiteral struct{}                              `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte                                `json:"-"` | ||||||
|  | 	XXX_sizecache        int32                                 `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *ContentManifestPayload) Reset()                    { *m = ContentManifestPayload{} } | func (m *ContentManifestPayload) Reset()         { *m = ContentManifestPayload{} } | ||||||
| func (m *ContentManifestPayload) String() string            { return proto.CompactTextString(m) } | func (m *ContentManifestPayload) String() string { return proto.CompactTextString(m) } | ||||||
| func (*ContentManifestPayload) ProtoMessage()               {} | func (*ContentManifestPayload) ProtoMessage()    {} | ||||||
| func (*ContentManifestPayload) Descriptor() ([]byte, []int) { return content_manifest_fileDescriptor0, []int{0} } | func (*ContentManifestPayload) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_e3cda137a29253ba, []int{0} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentManifestPayload) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentManifestPayload.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentManifestPayload.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentManifestPayload proto.InternalMessageInfo | ||||||
|  |  | ||||||
| func (m *ContentManifestPayload) GetMappings() []*ContentManifestPayload_FileMapping { | func (m *ContentManifestPayload) GetMappings() []*ContentManifestPayload_FileMapping { | ||||||
| 	if m != nil { | 	if m != nil { | ||||||
| @@ -31,23 +60,43 @@ func (m *ContentManifestPayload) GetMappings() []*ContentManifestPayload_FileMap | |||||||
| } | } | ||||||
|  |  | ||||||
| type ContentManifestPayload_FileMapping struct { | type ContentManifestPayload_FileMapping struct { | ||||||
| 	Filename         *string                                         `protobuf:"bytes,1,opt,name=filename" json:"filename,omitempty"` | 	Filename             *string                                         `protobuf:"bytes,1,opt,name=filename" json:"filename,omitempty"` | ||||||
| 	Size             *uint64                                         `protobuf:"varint,2,opt,name=size" json:"size,omitempty"` | 	Size                 *uint64                                         `protobuf:"varint,2,opt,name=size" json:"size,omitempty"` | ||||||
| 	Flags            *uint32                                         `protobuf:"varint,3,opt,name=flags" json:"flags,omitempty"` | 	Flags                *uint32                                         `protobuf:"varint,3,opt,name=flags" json:"flags,omitempty"` | ||||||
| 	ShaFilename      []byte                                          `protobuf:"bytes,4,opt,name=sha_filename" json:"sha_filename,omitempty"` | 	ShaFilename          []byte                                          `protobuf:"bytes,4,opt,name=sha_filename" json:"sha_filename,omitempty"` | ||||||
| 	ShaContent       []byte                                          `protobuf:"bytes,5,opt,name=sha_content" json:"sha_content,omitempty"` | 	ShaContent           []byte                                          `protobuf:"bytes,5,opt,name=sha_content" json:"sha_content,omitempty"` | ||||||
| 	Chunks           []*ContentManifestPayload_FileMapping_ChunkData `protobuf:"bytes,6,rep,name=chunks" json:"chunks,omitempty"` | 	Chunks               []*ContentManifestPayload_FileMapping_ChunkData `protobuf:"bytes,6,rep,name=chunks" json:"chunks,omitempty"` | ||||||
| 	Linktarget       *string                                         `protobuf:"bytes,7,opt,name=linktarget" json:"linktarget,omitempty"` | 	Linktarget           *string                                         `protobuf:"bytes,7,opt,name=linktarget" json:"linktarget,omitempty"` | ||||||
| 	XXX_unrecognized []byte                                          `json:"-"` | 	XXX_NoUnkeyedLiteral struct{}                                        `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte                                          `json:"-"` | ||||||
|  | 	XXX_sizecache        int32                                           `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *ContentManifestPayload_FileMapping) Reset()         { *m = ContentManifestPayload_FileMapping{} } | func (m *ContentManifestPayload_FileMapping) Reset()         { *m = ContentManifestPayload_FileMapping{} } | ||||||
| func (m *ContentManifestPayload_FileMapping) String() string { return proto.CompactTextString(m) } | func (m *ContentManifestPayload_FileMapping) String() string { return proto.CompactTextString(m) } | ||||||
| func (*ContentManifestPayload_FileMapping) ProtoMessage()    {} | func (*ContentManifestPayload_FileMapping) ProtoMessage()    {} | ||||||
| func (*ContentManifestPayload_FileMapping) Descriptor() ([]byte, []int) { | func (*ContentManifestPayload_FileMapping) Descriptor() ([]byte, []int) { | ||||||
| 	return content_manifest_fileDescriptor0, []int{0, 0} | 	return fileDescriptor_e3cda137a29253ba, []int{0, 0} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (m *ContentManifestPayload_FileMapping) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload_FileMapping.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload_FileMapping.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentManifestPayload_FileMapping.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload_FileMapping.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentManifestPayload_FileMapping.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentManifestPayload_FileMapping proto.InternalMessageInfo | ||||||
|  |  | ||||||
| func (m *ContentManifestPayload_FileMapping) GetFilename() string { | func (m *ContentManifestPayload_FileMapping) GetFilename() string { | ||||||
| 	if m != nil && m.Filename != nil { | 	if m != nil && m.Filename != nil { | ||||||
| 		return *m.Filename | 		return *m.Filename | ||||||
| @@ -98,12 +147,14 @@ func (m *ContentManifestPayload_FileMapping) GetLinktarget() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| type ContentManifestPayload_FileMapping_ChunkData struct { | type ContentManifestPayload_FileMapping_ChunkData struct { | ||||||
| 	Sha              []byte  `protobuf:"bytes,1,opt,name=sha" json:"sha,omitempty"` | 	Sha                  []byte   `protobuf:"bytes,1,opt,name=sha" json:"sha,omitempty"` | ||||||
| 	Crc              *uint32 `protobuf:"fixed32,2,opt,name=crc" json:"crc,omitempty"` | 	Crc                  *uint32  `protobuf:"fixed32,2,opt,name=crc" json:"crc,omitempty"` | ||||||
| 	Offset           *uint64 `protobuf:"varint,3,opt,name=offset" json:"offset,omitempty"` | 	Offset               *uint64  `protobuf:"varint,3,opt,name=offset" json:"offset,omitempty"` | ||||||
| 	CbOriginal       *uint32 `protobuf:"varint,4,opt,name=cb_original" json:"cb_original,omitempty"` | 	CbOriginal           *uint32  `protobuf:"varint,4,opt,name=cb_original" json:"cb_original,omitempty"` | ||||||
| 	CbCompressed     *uint32 `protobuf:"varint,5,opt,name=cb_compressed" json:"cb_compressed,omitempty"` | 	CbCompressed         *uint32  `protobuf:"varint,5,opt,name=cb_compressed" json:"cb_compressed,omitempty"` | ||||||
| 	XXX_unrecognized []byte  `json:"-"` | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *ContentManifestPayload_FileMapping_ChunkData) Reset() { | func (m *ContentManifestPayload_FileMapping_ChunkData) Reset() { | ||||||
| @@ -114,9 +165,27 @@ func (m *ContentManifestPayload_FileMapping_ChunkData) String() string { | |||||||
| } | } | ||||||
| func (*ContentManifestPayload_FileMapping_ChunkData) ProtoMessage() {} | func (*ContentManifestPayload_FileMapping_ChunkData) ProtoMessage() {} | ||||||
| func (*ContentManifestPayload_FileMapping_ChunkData) Descriptor() ([]byte, []int) { | func (*ContentManifestPayload_FileMapping_ChunkData) Descriptor() ([]byte, []int) { | ||||||
| 	return content_manifest_fileDescriptor0, []int{0, 0, 0} | 	return fileDescriptor_e3cda137a29253ba, []int{0, 0, 0} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData proto.InternalMessageInfo | ||||||
|  |  | ||||||
| func (m *ContentManifestPayload_FileMapping_ChunkData) GetSha() []byte { | func (m *ContentManifestPayload_FileMapping_ChunkData) GetSha() []byte { | ||||||
| 	if m != nil { | 	if m != nil { | ||||||
| 		return m.Sha | 		return m.Sha | ||||||
| @@ -153,22 +222,44 @@ func (m *ContentManifestPayload_FileMapping_ChunkData) GetCbCompressed() uint32 | |||||||
| } | } | ||||||
|  |  | ||||||
| type ContentManifestMetadata struct { | type ContentManifestMetadata struct { | ||||||
| 	DepotId            *uint32 `protobuf:"varint,1,opt,name=depot_id" json:"depot_id,omitempty"` | 	DepotId              *uint32  `protobuf:"varint,1,opt,name=depot_id" json:"depot_id,omitempty"` | ||||||
| 	GidManifest        *uint64 `protobuf:"varint,2,opt,name=gid_manifest" json:"gid_manifest,omitempty"` | 	GidManifest          *uint64  `protobuf:"varint,2,opt,name=gid_manifest" json:"gid_manifest,omitempty"` | ||||||
| 	CreationTime       *uint32 `protobuf:"varint,3,opt,name=creation_time" json:"creation_time,omitempty"` | 	CreationTime         *uint32  `protobuf:"varint,3,opt,name=creation_time" json:"creation_time,omitempty"` | ||||||
| 	FilenamesEncrypted *bool   `protobuf:"varint,4,opt,name=filenames_encrypted" json:"filenames_encrypted,omitempty"` | 	FilenamesEncrypted   *bool    `protobuf:"varint,4,opt,name=filenames_encrypted" json:"filenames_encrypted,omitempty"` | ||||||
| 	CbDiskOriginal     *uint64 `protobuf:"varint,5,opt,name=cb_disk_original" json:"cb_disk_original,omitempty"` | 	CbDiskOriginal       *uint64  `protobuf:"varint,5,opt,name=cb_disk_original" json:"cb_disk_original,omitempty"` | ||||||
| 	CbDiskCompressed   *uint64 `protobuf:"varint,6,opt,name=cb_disk_compressed" json:"cb_disk_compressed,omitempty"` | 	CbDiskCompressed     *uint64  `protobuf:"varint,6,opt,name=cb_disk_compressed" json:"cb_disk_compressed,omitempty"` | ||||||
| 	UniqueChunks       *uint32 `protobuf:"varint,7,opt,name=unique_chunks" json:"unique_chunks,omitempty"` | 	UniqueChunks         *uint32  `protobuf:"varint,7,opt,name=unique_chunks" json:"unique_chunks,omitempty"` | ||||||
| 	CrcEncrypted       *uint32 `protobuf:"varint,8,opt,name=crc_encrypted" json:"crc_encrypted,omitempty"` | 	CrcEncrypted         *uint32  `protobuf:"varint,8,opt,name=crc_encrypted" json:"crc_encrypted,omitempty"` | ||||||
| 	CrcClear           *uint32 `protobuf:"varint,9,opt,name=crc_clear" json:"crc_clear,omitempty"` | 	CrcClear             *uint32  `protobuf:"varint,9,opt,name=crc_clear" json:"crc_clear,omitempty"` | ||||||
| 	XXX_unrecognized   []byte  `json:"-"` | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *ContentManifestMetadata) Reset()                    { *m = ContentManifestMetadata{} } | func (m *ContentManifestMetadata) Reset()         { *m = ContentManifestMetadata{} } | ||||||
| func (m *ContentManifestMetadata) String() string            { return proto.CompactTextString(m) } | func (m *ContentManifestMetadata) String() string { return proto.CompactTextString(m) } | ||||||
| func (*ContentManifestMetadata) ProtoMessage()               {} | func (*ContentManifestMetadata) ProtoMessage()    {} | ||||||
| func (*ContentManifestMetadata) Descriptor() ([]byte, []int) { return content_manifest_fileDescriptor0, []int{1} } | func (*ContentManifestMetadata) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_e3cda137a29253ba, []int{1} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentManifestMetadata) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentManifestMetadata.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentManifestMetadata.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestMetadata) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentManifestMetadata.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestMetadata) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentManifestMetadata.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestMetadata) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentManifestMetadata.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentManifestMetadata proto.InternalMessageInfo | ||||||
|  |  | ||||||
| func (m *ContentManifestMetadata) GetDepotId() uint32 { | func (m *ContentManifestMetadata) GetDepotId() uint32 { | ||||||
| 	if m != nil && m.DepotId != nil { | 	if m != nil && m.DepotId != nil { | ||||||
| @@ -234,14 +325,36 @@ func (m *ContentManifestMetadata) GetCrcClear() uint32 { | |||||||
| } | } | ||||||
|  |  | ||||||
| type ContentManifestSignature struct { | type ContentManifestSignature struct { | ||||||
| 	Signature        []byte `protobuf:"bytes,1,opt,name=signature" json:"signature,omitempty"` | 	Signature            []byte   `protobuf:"bytes,1,opt,name=signature" json:"signature,omitempty"` | ||||||
| 	XXX_unrecognized []byte `json:"-"` | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *ContentManifestSignature) Reset()                    { *m = ContentManifestSignature{} } | func (m *ContentManifestSignature) Reset()         { *m = ContentManifestSignature{} } | ||||||
| func (m *ContentManifestSignature) String() string            { return proto.CompactTextString(m) } | func (m *ContentManifestSignature) String() string { return proto.CompactTextString(m) } | ||||||
| func (*ContentManifestSignature) ProtoMessage()               {} | func (*ContentManifestSignature) ProtoMessage()    {} | ||||||
| func (*ContentManifestSignature) Descriptor() ([]byte, []int) { return content_manifest_fileDescriptor0, []int{2} } | func (*ContentManifestSignature) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_e3cda137a29253ba, []int{2} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentManifestSignature) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentManifestSignature.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentManifestSignature.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestSignature) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentManifestSignature.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestSignature) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentManifestSignature.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentManifestSignature) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentManifestSignature.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentManifestSignature proto.InternalMessageInfo | ||||||
|  |  | ||||||
| func (m *ContentManifestSignature) GetSignature() []byte { | func (m *ContentManifestSignature) GetSignature() []byte { | ||||||
| 	if m != nil { | 	if m != nil { | ||||||
| @@ -250,40 +363,184 @@ func (m *ContentManifestSignature) GetSignature() []byte { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type ContentDeltaChunks struct { | ||||||
|  | 	DepotId              *uint32                          `protobuf:"varint,1,opt,name=depot_id" json:"depot_id,omitempty"` | ||||||
|  | 	ManifestIdSource     *uint64                          `protobuf:"varint,2,opt,name=manifest_id_source" json:"manifest_id_source,omitempty"` | ||||||
|  | 	ManifestIdTarget     *uint64                          `protobuf:"varint,3,opt,name=manifest_id_target" json:"manifest_id_target,omitempty"` | ||||||
|  | 	DeltaChunks          []*ContentDeltaChunks_DeltaChunk `protobuf:"bytes,4,rep,name=deltaChunks" json:"deltaChunks,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{}                         `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte                           `json:"-"` | ||||||
|  | 	XXX_sizecache        int32                            `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks) Reset()         { *m = ContentDeltaChunks{} } | ||||||
|  | func (m *ContentDeltaChunks) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*ContentDeltaChunks) ProtoMessage()    {} | ||||||
|  | func (*ContentDeltaChunks) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_e3cda137a29253ba, []int{3} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentDeltaChunks.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentDeltaChunks.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentDeltaChunks.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentDeltaChunks.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentDeltaChunks.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentDeltaChunks proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks) GetDepotId() uint32 { | ||||||
|  | 	if m != nil && m.DepotId != nil { | ||||||
|  | 		return *m.DepotId | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks) GetManifestIdSource() uint64 { | ||||||
|  | 	if m != nil && m.ManifestIdSource != nil { | ||||||
|  | 		return *m.ManifestIdSource | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks) GetManifestIdTarget() uint64 { | ||||||
|  | 	if m != nil && m.ManifestIdTarget != nil { | ||||||
|  | 		return *m.ManifestIdTarget | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks) GetDeltaChunks() []*ContentDeltaChunks_DeltaChunk { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.DeltaChunks | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ContentDeltaChunks_DeltaChunk struct { | ||||||
|  | 	ShaSource            []byte   `protobuf:"bytes,1,opt,name=sha_source" json:"sha_source,omitempty"` | ||||||
|  | 	ShaTarget            []byte   `protobuf:"bytes,2,opt,name=sha_target" json:"sha_target,omitempty"` | ||||||
|  | 	SizeOriginal         *uint32  `protobuf:"varint,3,opt,name=size_original" json:"size_original,omitempty"` | ||||||
|  | 	PatchMethod          *uint32  `protobuf:"varint,4,opt,name=patch_method" json:"patch_method,omitempty"` | ||||||
|  | 	Chunk                []byte   `protobuf:"bytes,5,opt,name=chunk" json:"chunk,omitempty"` | ||||||
|  | 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||||
|  | 	XXX_unrecognized     []byte   `json:"-"` | ||||||
|  | 	XXX_sizecache        int32    `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) Reset()         { *m = ContentDeltaChunks_DeltaChunk{} } | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*ContentDeltaChunks_DeltaChunk) ProtoMessage()    {} | ||||||
|  | func (*ContentDeltaChunks_DeltaChunk) Descriptor() ([]byte, []int) { | ||||||
|  | 	return fileDescriptor_e3cda137a29253ba, []int{3, 0} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) XXX_Unmarshal(b []byte) error { | ||||||
|  | 	return xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Unmarshal(m, b) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||||
|  | 	return xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Marshal(b, m, deterministic) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) XXX_Merge(src proto.Message) { | ||||||
|  | 	xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Merge(m, src) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) XXX_Size() int { | ||||||
|  | 	return xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Size(m) | ||||||
|  | } | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) XXX_DiscardUnknown() { | ||||||
|  | 	xxx_messageInfo_ContentDeltaChunks_DeltaChunk.DiscardUnknown(m) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var xxx_messageInfo_ContentDeltaChunks_DeltaChunk proto.InternalMessageInfo | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) GetShaSource() []byte { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.ShaSource | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) GetShaTarget() []byte { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.ShaTarget | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) GetSizeOriginal() uint32 { | ||||||
|  | 	if m != nil && m.SizeOriginal != nil { | ||||||
|  | 		return *m.SizeOriginal | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) GetPatchMethod() uint32 { | ||||||
|  | 	if m != nil && m.PatchMethod != nil { | ||||||
|  | 		return *m.PatchMethod | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *ContentDeltaChunks_DeltaChunk) GetChunk() []byte { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.Chunk | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	proto.RegisterType((*ContentManifestPayload)(nil), "ContentManifestPayload") | 	proto.RegisterType((*ContentManifestPayload)(nil), "ContentManifestPayload") | ||||||
| 	proto.RegisterType((*ContentManifestPayload_FileMapping)(nil), "ContentManifestPayload.FileMapping") | 	proto.RegisterType((*ContentManifestPayload_FileMapping)(nil), "ContentManifestPayload.FileMapping") | ||||||
| 	proto.RegisterType((*ContentManifestPayload_FileMapping_ChunkData)(nil), "ContentManifestPayload.FileMapping.ChunkData") | 	proto.RegisterType((*ContentManifestPayload_FileMapping_ChunkData)(nil), "ContentManifestPayload.FileMapping.ChunkData") | ||||||
| 	proto.RegisterType((*ContentManifestMetadata)(nil), "ContentManifestMetadata") | 	proto.RegisterType((*ContentManifestMetadata)(nil), "ContentManifestMetadata") | ||||||
| 	proto.RegisterType((*ContentManifestSignature)(nil), "ContentManifestSignature") | 	proto.RegisterType((*ContentManifestSignature)(nil), "ContentManifestSignature") | ||||||
|  | 	proto.RegisterType((*ContentDeltaChunks)(nil), "ContentDeltaChunks") | ||||||
|  | 	proto.RegisterType((*ContentDeltaChunks_DeltaChunk)(nil), "ContentDeltaChunks.DeltaChunk") | ||||||
| } | } | ||||||
|  |  | ||||||
| var content_manifest_fileDescriptor0 = []byte{ | func init() { proto.RegisterFile("content_manifest.proto", fileDescriptor_e3cda137a29253ba) } | ||||||
| 	// 409 bytes of a gzipped FileDescriptorProto |  | ||||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x91, 0xbd, 0x8e, 0xd4, 0x30, | var fileDescriptor_e3cda137a29253ba = []byte{ | ||||||
| 	0x14, 0x85, 0xc9, 0xfc, 0x26, 0x37, 0x09, 0x5a, 0xbc, 0xb0, 0x58, 0x43, 0x83, 0x96, 0x66, 0x9b, | 	// 508 bytes of a gzipped FileDescriptorProto | ||||||
| 	0x4d, 0x81, 0x44, 0x49, 0xc3, 0x22, 0x44, 0x33, 0x12, 0x12, 0x0f, 0x10, 0x5d, 0x1c, 0x27, 0x6b, | 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcd, 0x8e, 0xd3, 0x30, | ||||||
| 	0x4d, 0x62, 0x07, 0xdb, 0x29, 0x96, 0x8a, 0x17, 0xe1, 0x0d, 0x91, 0x78, 0x05, 0x6c, 0x27, 0x99, | 	0x10, 0xc7, 0x49, 0xbf, 0x3b, 0x6d, 0x50, 0xf1, 0xb2, 0x8b, 0x55, 0x24, 0x54, 0x95, 0x4b, 0x2f, | ||||||
| 	0x1d, 0x8d, 0x28, 0x28, 0xcf, 0xf1, 0xb5, 0xcf, 0x77, 0x8f, 0xe1, 0x8a, 0x29, 0x69, 0xb9, 0xb4, | 	0x9b, 0x03, 0x88, 0x23, 0x17, 0x76, 0x85, 0xb8, 0x54, 0x42, 0xe2, 0x01, 0x2c, 0xd7, 0x76, 0x53, | ||||||
| 	0x65, 0x87, 0x52, 0xd4, 0xdc, 0xd8, 0xa2, 0xd7, 0xca, 0xaa, 0xeb, 0x3f, 0x0b, 0xb8, 0xba, 0x1b, | 	0xab, 0x89, 0x1d, 0x6c, 0xe7, 0xb0, 0x9c, 0xb8, 0xf0, 0x18, 0xbc, 0x21, 0x12, 0xaf, 0x80, 0xec, | ||||||
| 	0x8f, 0xf6, 0xd3, 0xc9, 0x17, 0x7c, 0x68, 0x15, 0x56, 0xe4, 0x1d, 0xc4, 0x1d, 0xf6, 0xbd, 0x90, | 	0x38, 0x4d, 0x55, 0xf6, 0xb0, 0xb7, 0xcc, 0x47, 0x3c, 0xbf, 0xf9, 0xcf, 0x1f, 0x6e, 0x98, 0x56, | ||||||
| 	0x8d, 0xa1, 0xd1, 0xeb, 0xe5, 0x4d, 0xfa, 0xf6, 0x4d, 0xf1, 0xef, 0xd1, 0xe2, 0x93, 0x68, 0xf9, | 	0x4e, 0x28, 0x47, 0x4a, 0xaa, 0xe4, 0x5e, 0x58, 0x97, 0x55, 0x46, 0x3b, 0xbd, 0xfe, 0xdb, 0x83, | ||||||
| 	0x7e, 0x9c, 0xdd, 0xfd, 0x5a, 0x40, 0x7a, 0xa2, 0xc9, 0x05, 0xc4, 0xb5, 0x93, 0x12, 0x3b, 0xee, | 	0x9b, 0xbb, 0xa6, 0xb4, 0x8d, 0x95, 0xaf, 0xf4, 0xa1, 0xd0, 0x94, 0xa3, 0x0f, 0x30, 0x29, 0x69, | ||||||
| 	0x9e, 0x89, 0x6e, 0x12, 0x92, 0xc1, 0xca, 0x88, 0x1f, 0x9c, 0x2e, 0x9c, 0x5a, 0x91, 0x1c, 0xd6, | 	0x55, 0x49, 0x95, 0x5b, 0x9c, 0xac, 0xfa, 0x9b, 0xd9, 0xbb, 0xb7, 0xd9, 0xe3, 0xad, 0xd9, 0x67, | ||||||
| 	0x75, 0x8b, 0x2e, 0x63, 0xe9, 0x64, 0x4e, 0x9e, 0x43, 0x66, 0xee, 0xb1, 0x3c, 0x5e, 0x59, 0x39, | 	0x59, 0x88, 0x6d, 0xd3, 0xbb, 0xfc, 0xdd, 0x83, 0xd9, 0x59, 0x8c, 0x16, 0x30, 0xd9, 0xcb, 0x42, | ||||||
| 	0x37, 0x23, 0x97, 0x90, 0x7a, 0x77, 0x5a, 0x82, 0xae, 0x83, 0xf9, 0x1e, 0x36, 0xec, 0x7e, 0x90, | 	0x28, 0x5a, 0x0a, 0x9c, 0xac, 0x92, 0xcd, 0x14, 0xcd, 0x61, 0x60, 0xe5, 0x0f, 0x81, 0x7b, 0xab, | ||||||
| 	0x07, 0x43, 0x37, 0x01, 0xef, 0xf6, 0x3f, 0xf0, 0x8a, 0x3b, 0x7f, 0xe3, 0x23, 0x5a, 0x24, 0x04, | 	0x64, 0x33, 0x40, 0x29, 0x0c, 0xf7, 0x05, 0xcd, 0x2d, 0xee, 0xaf, 0x92, 0x4d, 0x8a, 0x5e, 0xc2, | ||||||
| 	0xa0, 0x15, 0xf2, 0x60, 0x51, 0x37, 0xdc, 0xd2, 0xad, 0x47, 0xdb, 0x21, 0x24, 0x8f, 0x03, 0x29, | 	0xdc, 0x1e, 0x28, 0x39, 0xfd, 0x32, 0x58, 0x25, 0x9b, 0x39, 0xba, 0x82, 0x99, 0xcf, 0xc6, 0x25, | ||||||
| 	0x2c, 0x5d, 0x68, 0x80, 0xce, 0xbc, 0x60, 0x9a, 0x05, 0xe6, 0x2d, 0x79, 0x0a, 0x1b, 0x55, 0xd7, | 	0xf0, 0x30, 0x24, 0x3f, 0xc2, 0x88, 0x1d, 0x6a, 0x75, 0xb4, 0x78, 0x14, 0xf0, 0x6e, 0x9f, 0x80, | ||||||
| 	0xc6, 0x5d, 0x5b, 0x86, 0x1d, 0x1c, 0x1e, 0xfb, 0x56, 0x2a, 0x2d, 0x1a, 0x21, 0xb1, 0x0d, 0xcc, | 	0x97, 0xdd, 0xf9, 0x3f, 0xee, 0xa9, 0xa3, 0x08, 0x01, 0x14, 0x52, 0x1d, 0x1d, 0x35, 0xb9, 0x70, | ||||||
| 	0x39, 0x79, 0x01, 0xb9, 0x33, 0x99, 0xea, 0x7a, 0xcd, 0x8d, 0xe1, 0x55, 0xa0, 0xce, 0xaf, 0x7f, | 	0x78, 0xec, 0xd1, 0x96, 0x14, 0xa6, 0x5d, 0xc3, 0x0c, 0xfa, 0xf6, 0x40, 0x03, 0xf4, 0xdc, 0x07, | ||||||
| 	0x47, 0xf0, 0xf2, 0x8c, 0x73, 0xcf, 0x2d, 0x56, 0x3e, 0xd1, 0x75, 0x55, 0xf1, 0x5e, 0xd9, 0x52, | 	0xcc, 0xb0, 0xc0, 0x3c, 0x46, 0xcf, 0x61, 0xa4, 0xf7, 0x7b, 0x2b, 0x5c, 0x80, 0x1e, 0x78, 0x3c, | ||||||
| 	0x54, 0x21, 0x36, 0xd4, 0xd1, 0x88, 0xea, 0xf8, 0x6b, 0x53, 0x67, 0xfe, 0x69, 0xcd, 0xd1, 0x0a, | 	0xb6, 0x23, 0xda, 0xc8, 0x5c, 0x2a, 0x5a, 0x04, 0xe6, 0x14, 0x5d, 0x43, 0xca, 0x76, 0x84, 0xe9, | ||||||
| 	0x25, 0x4b, 0x2b, 0x5c, 0x4b, 0x63, 0x77, 0xaf, 0xe0, 0x72, 0xee, 0xcd, 0x94, 0x5c, 0x32, 0xfd, | 	0xb2, 0x32, 0xc2, 0x5a, 0xc1, 0x03, 0x75, 0xba, 0xfe, 0x93, 0xc0, 0xab, 0x0b, 0xce, 0xad, 0x70, | ||||||
| 	0xd0, 0x5b, 0x97, 0xeb, 0x71, 0x62, 0x42, 0xe1, 0xc2, 0xe1, 0x54, 0xc2, 0x1c, 0x1e, 0x41, 0xd7, | 	0x94, 0xfb, 0x89, 0x0b, 0x98, 0x70, 0x51, 0x69, 0x47, 0x24, 0x0f, 0x63, 0x83, 0x1c, 0xb9, 0xe4, | ||||||
| 	0xe1, 0xb5, 0x1d, 0x90, 0xf9, 0xe4, 0x84, 0x76, 0x33, 0x27, 0x0d, 0x52, 0x7c, 0x1f, 0x78, 0x39, | 	0xa7, 0xab, 0x45, 0xcd, 0xfc, 0xd3, 0x46, 0x50, 0x27, 0xb5, 0x22, 0x4e, 0x96, 0x22, 0x6a, 0xf7, | ||||||
| 	0x55, 0xbd, 0x3d, 0xee, 0xa6, 0xd9, 0x49, 0x46, 0x1c, 0xec, 0x67, 0x90, 0x78, 0x9b, 0xb5, 0x1c, | 	0x1a, 0xae, 0x5a, 0xdd, 0x2c, 0x11, 0x8a, 0x99, 0x87, 0xca, 0x09, 0x1e, 0x70, 0x26, 0x08, 0xc3, | ||||||
| 	0x35, 0x4d, 0xc2, 0xba, 0xb7, 0x40, 0xcf, 0xb6, 0xfd, 0x2a, 0x1a, 0x89, 0x76, 0xd0, 0xdc, 0x8f, | 	0x82, 0xed, 0x08, 0x97, 0xf6, 0xd8, 0x81, 0x0e, 0xc3, 0x6b, 0x4b, 0x40, 0x6d, 0xe5, 0x8c, 0x76, | ||||||
| 	0x9b, 0x59, 0x8c, 0x35, 0x7f, 0x58, 0x7f, 0x8e, 0x7e, 0x46, 0x4f, 0xfe, 0x06, 0x00, 0x00, 0xff, | 	0xd4, 0x4e, 0xaa, 0x95, 0xfc, 0x5e, 0x0b, 0x12, 0xa5, 0x1e, 0x9f, 0x76, 0x33, 0xec, 0x6c, 0xc6, | ||||||
| 	0xff, 0xc6, 0x87, 0xdb, 0xe6, 0xaf, 0x02, 0x00, 0x00, | 	0x24, 0xa4, 0x5f, 0xc0, 0xd4, 0xa7, 0x59, 0x21, 0xa8, 0xc1, 0xd3, 0xb0, 0xee, 0x2d, 0xe0, 0x8b, | ||||||
|  | 	0x6d, 0xbf, 0xc9, 0x5c, 0x51, 0x57, 0x1b, 0xe1, 0xdb, 0x6d, 0x1b, 0x34, 0x32, 0xaf, 0x7f, 0xf5, | ||||||
|  | 	0x00, 0xc5, 0xfe, 0x7b, 0x51, 0x38, 0x1a, 0xae, 0x61, 0x1f, 0x11, 0x66, 0x09, 0xa8, 0x15, 0x85, | ||||||
|  | 	0x48, 0x4e, 0xac, 0xae, 0x0d, 0x6b, 0x2d, 0x75, 0x51, 0x8b, 0x17, 0x6e, 0x4e, 0xf5, 0x1e, 0x66, | ||||||
|  | 	0xbc, 0x7b, 0x18, 0x0f, 0x82, 0x73, 0xde, 0x64, 0xff, 0xcf, 0xcc, 0xba, 0xef, 0x65, 0x05, 0xd0, | ||||||
|  | 	0x45, 0xde, 0x38, 0xde, 0x8c, 0x71, 0x64, 0x63, 0x8f, 0x98, 0x8b, 0xa3, 0x7a, 0x21, 0x77, 0x0d, | ||||||
|  | 	0xa9, 0xf7, 0x79, 0x27, 0xf7, 0xc9, 0xe1, 0x15, 0x75, 0xec, 0x40, 0x4a, 0xe1, 0x0e, 0x9a, 0x47, | ||||||
|  | 	0xb7, 0xa4, 0x30, 0x0c, 0x0a, 0x37, 0xde, 0xfe, 0x34, 0xfc, 0x92, 0xfc, 0x4c, 0x9e, 0xfd, 0x0b, | ||||||
|  | 	0x00, 0x00, 0xff, 0xff, 0x00, 0x92, 0x22, 0xd7, 0xb7, 0x03, 0x00, 0x00, | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										6106
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/steamlang/enums.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6106
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/steamlang/enums.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												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