forked from lug/matterbridge
		
	Compare commits
	
		
			137 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					39f4fb3446 | ||
| 
						 | 
					56159b9bce | ||
| 
						 | 
					b2af76e7dc | ||
| 
						 | 
					491fe35397 | ||
| 
						 | 
					b451285af7 | ||
| 
						 | 
					63a1847cdc | ||
| 
						 | 
					4e50fd8649 | ||
| 
						 | 
					dfdffa0027 | ||
| 
						 | 
					ebd2073144 | ||
| 
						 | 
					1e94b716fb | ||
| 
						 | 
					2a41abb3d1 | ||
| 
						 | 
					2d2bebe976 | ||
| 
						 | 
					e1629994bd | ||
| 
						 | 
					e3d8fe4fd8 | ||
| 
						 | 
					23d8742f0d | ||
| 
						 | 
					3b6a8be07b | ||
| 
						 | 
					71a5b72aff | ||
| 
						 | 
					213bf349c3 | ||
| 
						 | 
					a94fe55886 | ||
| 
						 | 
					9b22f16497 | ||
| 
						 | 
					2977a5957e | ||
| 
						 | 
					f70d1c897a | ||
| 
						 | 
					a4a3525265 | ||
| 
						 | 
					a6dd8446e4 | ||
| 
						 | 
					7bf9e1cfb3 | ||
| 
						 | 
					f291832a77 | ||
| 
						 | 
					1fee323247 | ||
| 
						 | 
					a41accd033 | ||
| 
						 | 
					37f7caf7f3 | ||
| 
						 | 
					5847f7758c | ||
| 
						 | 
					bce736993e | ||
| 
						 | 
					5636992446 | ||
| 
						 | 
					f996a2b7ae | ||
| 
						 | 
					587de96ab3 | ||
| 
						 | 
					80eb1cd202 | ||
| 
						 | 
					bbf594c815 | ||
| 
						 | 
					2f0f2ee40d | ||
| 
						 | 
					96022d3aaf | ||
| 
						 | 
					8eb5e3cbf8 | ||
| 
						 | 
					ddc2625934 | ||
| 
						 | 
					7f7ca697a0 | ||
| 
						 | 
					900375679b | ||
| 
						 | 
					9440b9e313 | ||
| 
						 | 
					393f9e998b | ||
| 
						 | 
					ba0bfe70a8 | ||
| 
						 | 
					3c4a3e3f75 | ||
| 
						 | 
					274fb09ed4 | ||
| 
						 | 
					d44598a900 | ||
| 
						 | 
					c9cfa59f54 | ||
| 
						 | 
					7062234331 | ||
| 
						 | 
					9754569525 | ||
| 
						 | 
					52a071e34d | ||
| 
						 | 
					2d8f749e36 | ||
| 
						 | 
					a18cb74f03 | ||
| 
						 | 
					6c442e239d | ||
| 
						 | 
					eaf92fca4d | ||
| 
						 | 
					06b7bad714 | ||
| 
						 | 
					19eec2ed03 | ||
| 
						 | 
					d99c54343a | ||
| 
						 | 
					308a110000 | ||
| 
						 | 
					4f406b2ce6 | ||
| 
						 | 
					e564c555d7 | ||
| 
						 | 
					f7ec9af9e8 | ||
| 
						 | 
					4d93a774ce | ||
| 
						 | 
					2595dd30bf | ||
| 
						 | 
					9190365289 | ||
| 
						 | 
					57794b3b9f | ||
| 
						 | 
					3c36f651be | ||
| 
						 | 
					8e6ddadba2 | ||
| 
						 | 
					8a87a71927 | ||
| 
						 | 
					0047e6f523 | ||
| 
						 | 
					7183095a28 | ||
| 
						 | 
					13c90893c7 | ||
| 
						 | 
					976fbcd07f | ||
| 
						 | 
					d97b077e85 | ||
| 
						 | 
					8950575bfb | ||
| 
						 | 
					11fc4c286f | ||
| 
						 | 
					8d08e348a9 | ||
| 
						 | 
					a18807f19e | ||
| 
						 | 
					29f658fd3c | ||
| 
						 | 
					a30bb8fed0 | ||
| 
						 | 
					092ca1cd67 | ||
| 
						 | 
					0df2539641 | ||
| 
						 | 
					0f2d8a599c | ||
| 
						 | 
					54b3143a1d | ||
| 
						 | 
					148f7d2a91 | ||
| 
						 | 
					1aa662f763 | ||
| 
						 | 
					0b86b88de7 | ||
| 
						 | 
					98033b1ba7 | ||
| 
						 | 
					2b7eab629d | ||
| 
						 | 
					0e4973e15c | ||
| 
						 | 
					af0acf0dae | ||
| 
						 | 
					76e5fe5a87 | ||
| 
						 | 
					802c80f40c | ||
| 
						 | 
					a51c5bd905 | ||
| 
						 | 
					8c68556f52 | ||
| 
						 | 
					cca1ea2404 | ||
| 
						 | 
					281016a501 | ||
| 
						 | 
					d4acdf2f89 | ||
| 
						 | 
					0951e75c85 | ||
| 
						 | 
					6b017b226a | ||
| 
						 | 
					9e3bd7398c | ||
| 
						 | 
					79f764c7a8 | ||
| 
						 | 
					b5dc4353fb | ||
| 
						 | 
					2fbac73c29 | ||
| 
						 | 
					6616d105d1 | ||
| 
						 | 
					6b4b19194e | ||
| 
						 | 
					9785edd263 | ||
| 
						 | 
					2a0bc11b68 | ||
| 
						 | 
					dd0325a88d | ||
| 
						 | 
					20783c0978 | ||
| 
						 | 
					3f06a40bd5 | ||
| 
						 | 
					68f43985ad | ||
| 
						 | 
					915ca8f817 | ||
| 
						 | 
					a65a81610b | ||
| 
						 | 
					8eb6ed5639 | ||
| 
						 | 
					795a8705c3 | ||
| 
						 | 
					3af0dc3b3a | ||
| 
						 | 
					9cf9b958a3 | ||
| 
						 | 
					3ac2ba8d5a | ||
| 
						 | 
					d893421c7b | ||
| 
						 | 
					250b3bb579 | ||
| 
						 | 
					e9edbfc051 | ||
| 
						 | 
					e343db6f72 | ||
| 
						 | 
					4d57d66f85 | ||
| 
						 | 
					54ed6320c2 | ||
| 
						 | 
					23083f3ae0 | ||
| 
						 | 
					1985873494 | ||
| 
						 | 
					8ae5917659 | ||
| 
						 | 
					c91bfd08d8 | ||
| 
						 | 
					49110a5872 | ||
| 
						 | 
					c01c8edeb8 | ||
| 
						 | 
					ff8cf067b8 | ||
| 
						 | 
					1420f68050 | ||
| 
						 | 
					c0be3e585a | ||
| 
						 | 
					3049ef9151 | ||
| 
						 | 
					4be00bbe6b | 
							
								
								
									
										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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								.github/workflows/development.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								.github/workflows/development.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					name: Development
 | 
				
			||||||
 | 
					on: [push, pull_request]
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  lint:
 | 
				
			||||||
 | 
					    name: golangci-lint
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          fetch-depth: 20
 | 
				
			||||||
 | 
					      - name: Run golangci-lint
 | 
				
			||||||
 | 
					        uses: golangci/golangci-lint-action@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          version: v1.29
 | 
				
			||||||
 | 
					          args: "-v --new-from-rev HEAD~5"
 | 
				
			||||||
 | 
					  test-build-upload:
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        go-version: [1.14.x, 1.15.x]
 | 
				
			||||||
 | 
					        platform: [ubuntu-latest]
 | 
				
			||||||
 | 
					    runs-on: ${{ matrix.platform }}
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - name: Install Go
 | 
				
			||||||
 | 
					      uses: actions/setup-go@v2
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        go-version: ${{ matrix.go-version }}
 | 
				
			||||||
 | 
					    - name: Checkout code
 | 
				
			||||||
 | 
					      uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					    - name: Test
 | 
				
			||||||
 | 
					      run: go test ./... -mod=vendor
 | 
				
			||||||
 | 
					    - name: Build
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        mkdir -p output/{win,lin,arm,mac}
 | 
				
			||||||
 | 
					        VERSION=$(git describe --tags)
 | 
				
			||||||
 | 
					        GOOS=linux GOARCH=amd64 go build -mod=vendor -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/lin/matterbridge-$VERSION-linux-amd64
 | 
				
			||||||
 | 
					        GOOS=windows GOARCH=amd64 go build -mod=vendor -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
 | 
				
			||||||
 | 
					        GOOS=darwin GOARCH=amd64 go build -mod=vendor -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
 | 
				
			||||||
 | 
					    - name: Upload linux 64-bit
 | 
				
			||||||
 | 
					      if: startsWith(matrix.go-version,'1.15')
 | 
				
			||||||
 | 
					      uses: actions/upload-artifact@v2
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        name: matterbridge-linux-64bit
 | 
				
			||||||
 | 
					        path: output/lin
 | 
				
			||||||
 | 
					    - name: Upload windows 64-bit
 | 
				
			||||||
 | 
					      if: startsWith(matrix.go-version,'1.15')
 | 
				
			||||||
 | 
					      uses: actions/upload-artifact@v2
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        name: matterbridge-windows-64bit
 | 
				
			||||||
 | 
					        path: output/win
 | 
				
			||||||
 | 
					    - name: Upload darwin 64-bit
 | 
				
			||||||
 | 
					      if: startsWith(matrix.go-version,'1.15')
 | 
				
			||||||
 | 
					      uses: actions/upload-artifact@v2
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        name: matterbridge-darwin-64bit
 | 
				
			||||||
 | 
					        path: output/mac
 | 
				
			||||||
@@ -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
 | 
				
			||||||
@@ -175,7 +175,14 @@ linters:
 | 
				
			|||||||
    - maligned
 | 
					    - maligned
 | 
				
			||||||
    - prealloc
 | 
					    - prealloc
 | 
				
			||||||
    - wsl
 | 
					    - wsl
 | 
				
			||||||
 | 
					    - gomnd
 | 
				
			||||||
 | 
					    - godox
 | 
				
			||||||
 | 
					    - goerr113
 | 
				
			||||||
 | 
					    - testpackage
 | 
				
			||||||
 | 
					    - godot
 | 
				
			||||||
 | 
					    - interfacer
 | 
				
			||||||
 | 
					    - goheader
 | 
				
			||||||
 | 
					    - noctx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# rules to deal with reported isues
 | 
					# rules to deal with reported isues
 | 
				
			||||||
issues:
 | 
					issues:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										56
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,56 +0,0 @@
 | 
				
			|||||||
language: go
 | 
					 | 
				
			||||||
go_import_path: github.com/42wim/matterbridge
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# We have everything vendored so this helps TravisCI not run `go get ...`.
 | 
					 | 
				
			||||||
install: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
git:
 | 
					 | 
				
			||||||
  depth: 200
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
notifications:
 | 
					 | 
				
			||||||
  email: false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
branches:
 | 
					 | 
				
			||||||
  only:
 | 
					 | 
				
			||||||
  - master
 | 
					 | 
				
			||||||
  - /.*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jobs:
 | 
					 | 
				
			||||||
  include:
 | 
					 | 
				
			||||||
  - stage: lint
 | 
					 | 
				
			||||||
    # Run linting in one Go environment only.
 | 
					 | 
				
			||||||
    script: ./ci/lint.sh
 | 
					 | 
				
			||||||
    go: 1.13.x
 | 
					 | 
				
			||||||
    env:
 | 
					 | 
				
			||||||
    - GO111MODULE=on
 | 
					 | 
				
			||||||
    - GOLANGCI_VERSION="v1.21.0"
 | 
					 | 
				
			||||||
  - stage: test
 | 
					 | 
				
			||||||
    # Run tests in a combination of Go environments.
 | 
					 | 
				
			||||||
    script: ./ci/test.sh
 | 
					 | 
				
			||||||
    go: 1.12.x
 | 
					 | 
				
			||||||
    env:
 | 
					 | 
				
			||||||
    - GO111MODULE=off
 | 
					 | 
				
			||||||
  - script: ./ci/test.sh
 | 
					 | 
				
			||||||
    go: 1.12.x
 | 
					 | 
				
			||||||
    env:
 | 
					 | 
				
			||||||
    - GO111MODULE=on
 | 
					 | 
				
			||||||
  - script: ./ci/test.sh
 | 
					 | 
				
			||||||
    go: 1.13.x
 | 
					 | 
				
			||||||
    env:
 | 
					 | 
				
			||||||
    - GO111MODULE=on
 | 
					 | 
				
			||||||
    - REPORT_COVERAGE=1
 | 
					 | 
				
			||||||
    - BINDEPLOY=1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
before_deploy: /bin/bash ci/bintray.sh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
deploy:
 | 
					 | 
				
			||||||
  on:
 | 
					 | 
				
			||||||
    all_branches: true
 | 
					 | 
				
			||||||
    condition: $BINDEPLOY = 1
 | 
					 | 
				
			||||||
  provider: bintray
 | 
					 | 
				
			||||||
  edge:
 | 
					 | 
				
			||||||
    branch: v1.8.47
 | 
					 | 
				
			||||||
  file: ci/deploy.json
 | 
					 | 
				
			||||||
  user: 42wim
 | 
					 | 
				
			||||||
  key:
 | 
					 | 
				
			||||||
    secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI="
 | 
					 | 
				
			||||||
							
								
								
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,11 +1,16 @@
 | 
				
			|||||||
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 mailcap \
 | 
					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
 | 
				
			||||||
 | 
					RUN mkdir /etc/matterbridge \
 | 
				
			||||||
 | 
					  && touch /etc/matterbridge/matterbridge.toml \
 | 
				
			||||||
 | 
					  && ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml
 | 
				
			||||||
 | 
					ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								README.md
									
									
									
									
									
								
							@@ -9,26 +9,26 @@ Letting people be where they want to be.<br />
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   <sup>
 | 
					   <sup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Discord][mb-discord] |
 | 
				
			||||||
[Gitter][mb-gitter] |
 | 
					[Gitter][mb-gitter] |
 | 
				
			||||||
[IRC][mb-irc] |
 | 
					[IRC][mb-irc] |
 | 
				
			||||||
[Discord][mb-discord] |
 | 
					[Keybase][mb-keybase] |
 | 
				
			||||||
[Matrix][mb-matrix] |
 | 
					[Matrix][mb-matrix] |
 | 
				
			||||||
[Slack][mb-slack] |
 | 
					 | 
				
			||||||
[Mattermost][mb-mattermost] |
 | 
					[Mattermost][mb-mattermost] |
 | 
				
			||||||
 | 
					[MSTeams][mb-msteams] |
 | 
				
			||||||
[Rocket.Chat][mb-rocketchat] |
 | 
					[Rocket.Chat][mb-rocketchat] |
 | 
				
			||||||
[XMPP][mb-xmpp] |
 | 
					[Slack][mb-slack] |
 | 
				
			||||||
 | 
					[Telegram][mb-telegram] |
 | 
				
			||||||
[Twitch][mb-twitch] |
 | 
					[Twitch][mb-twitch] |
 | 
				
			||||||
[WhatsApp][mb-whatsapp] |
 | 
					[WhatsApp][mb-whatsapp] |
 | 
				
			||||||
 | 
					[XMPP][mb-xmpp] |
 | 
				
			||||||
[Zulip][mb-zulip] |
 | 
					[Zulip][mb-zulip] |
 | 
				
			||||||
[Telegram][mb-telegram] |
 | 
					 | 
				
			||||||
[Keybase][mb-keybase] |
 | 
					 | 
				
			||||||
And more...
 | 
					And more...
 | 
				
			||||||
</sup>
 | 
					</sup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://github.com/42wim/matterbridge/releases/latest)
 | 
					[](https://github.com/42wim/matterbridge/releases/latest)
 | 
				
			||||||
[](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 />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,28 +44,34 @@ And more...
 | 
				
			|||||||
  </a>
 | 
					  </a>
 | 
				
			||||||
</p>
 | 
					</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Table of Contents
 | 
					# Table of Contents
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Features](https://github.com/42wim/matterbridge/wiki/Features)
 | 
					- [matterbridge](#matterbridge)
 | 
				
			||||||
  - [Natively supported](#natively-supported)
 | 
					- [Table of Contents](#table-of-contents)
 | 
				
			||||||
  - [3rd party via matterbridge api](#3rd-party-via-matterbridge-api)
 | 
					  - [Features](#features)
 | 
				
			||||||
  - [API](#API)
 | 
					    - [Natively supported](#natively-supported)
 | 
				
			||||||
- [Chat with us](#chat-with-us)
 | 
					    - [3rd party via matterbridge api](#3rd-party-via-matterbridge-api)
 | 
				
			||||||
- [Screenshots](https://github.com/42wim/matterbridge/wiki/)
 | 
					    - [API](#api)
 | 
				
			||||||
- [Installing/upgrading](#installing--upgrading)
 | 
					  - [Chat with us](#chat-with-us)
 | 
				
			||||||
  - [Binaries](#binaries)
 | 
					  - [Screenshots](#screenshots)
 | 
				
			||||||
- [Building](#building)
 | 
					  - [Installing / upgrading](#installing--upgrading)
 | 
				
			||||||
- [Configuration](#configuration)
 | 
					    - [Binaries](#binaries)
 | 
				
			||||||
  - [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config)
 | 
					    - [Packages](#packages)
 | 
				
			||||||
  - [Settings](#settings)
 | 
					  - [Building](#building)
 | 
				
			||||||
  - [Examples](#examples)
 | 
					  - [Configuration](#configuration)
 | 
				
			||||||
- [Running](#running)
 | 
					    - [Basic configuration](#basic-configuration)
 | 
				
			||||||
  - [Docker](#docker)
 | 
					    - [Settings](#settings)
 | 
				
			||||||
- [Changelog](#changelog)
 | 
					    - [Advanced configuration](#advanced-configuration)
 | 
				
			||||||
- [FAQ](#faq)
 | 
					    - [Examples](#examples)
 | 
				
			||||||
- [Related projects](#related-projects)
 | 
					      - [Bridge mattermost (off-topic) - irc (#testing)](#bridge-mattermost-off-topic---irc-testing)
 | 
				
			||||||
- [Articles](#articles)
 | 
					      - [Bridge slack (#general) - discord (general)](#bridge-slack-general---discord-general)
 | 
				
			||||||
- [Thanks](#thanks)
 | 
					  - [Running](#running)
 | 
				
			||||||
 | 
					    - [Docker](#docker)
 | 
				
			||||||
 | 
					  - [Changelog](#changelog)
 | 
				
			||||||
 | 
					  - [FAQ](#faq)
 | 
				
			||||||
 | 
					  - [Related projects](#related-projects)
 | 
				
			||||||
 | 
					  - [Articles](#articles)
 | 
				
			||||||
 | 
					  - [Thanks](#thanks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Features
 | 
					## Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,29 +86,32 @@ And more...
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### Natively supported
 | 
					### Natively supported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
 | 
					 | 
				
			||||||
- [IRC](http://www.mirc.com/servers.html)
 | 
					 | 
				
			||||||
- [XMPP](https://xmpp.org)
 | 
					 | 
				
			||||||
- [Gitter](https://gitter.im)
 | 
					 | 
				
			||||||
- [Slack](https://slack.com)
 | 
					 | 
				
			||||||
- [Discord](https://discordapp.com)
 | 
					- [Discord](https://discordapp.com)
 | 
				
			||||||
- [Telegram](https://telegram.org)
 | 
					- [Gitter](https://gitter.im)
 | 
				
			||||||
- [Rocket.chat](https://rocket.chat)
 | 
					- [IRC](http://www.mirc.com/servers.html)
 | 
				
			||||||
- [Matrix](https://matrix.org)
 | 
					 | 
				
			||||||
- [Steam](https://store.steampowered.com/)
 | 
					 | 
				
			||||||
- [Twitch](https://twitch.tv)
 | 
					 | 
				
			||||||
- [Ssh-chat](https://github.com/shazow/ssh-chat)
 | 
					 | 
				
			||||||
- [WhatsApp](https://www.whatsapp.com/)
 | 
					 | 
				
			||||||
- [Zulip](https://zulipchat.com)
 | 
					 | 
				
			||||||
- [Keybase](https://keybase.io)
 | 
					- [Keybase](https://keybase.io)
 | 
				
			||||||
 | 
					- [Matrix](https://matrix.org)
 | 
				
			||||||
 | 
					- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
 | 
				
			||||||
 | 
					- [Microsoft Teams](https://teams.microsoft.com)
 | 
				
			||||||
 | 
					- [Nextcloud Talk](https://nextcloud.com/talk/)
 | 
				
			||||||
 | 
					- [Rocket.chat](https://rocket.chat)
 | 
				
			||||||
 | 
					- [Slack](https://slack.com)
 | 
				
			||||||
 | 
					- [Ssh-chat](https://github.com/shazow/ssh-chat)
 | 
				
			||||||
 | 
					- [Steam](https://store.steampowered.com/)
 | 
				
			||||||
 | 
					- [Telegram](https://telegram.org)
 | 
				
			||||||
 | 
					- [Twitch](https://twitch.tv)
 | 
				
			||||||
 | 
					- [WhatsApp](https://www.whatsapp.com/)
 | 
				
			||||||
 | 
					- [XMPP](https://xmpp.org)
 | 
				
			||||||
 | 
					- [Zulip](https://zulipchat.com)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 3rd party via matterbridge api
 | 
					### 3rd party via matterbridge api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [Discourse](https://github.com/DeclanHoare/matterbabble)
 | 
				
			||||||
 | 
					- [Facebook messenger](https://github.com/VictorNine/fbridge)
 | 
				
			||||||
- [Minecraft](https://github.com/elytra/MatterLink)
 | 
					- [Minecraft](https://github.com/elytra/MatterLink)
 | 
				
			||||||
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
 | 
					- [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)
 | 
					- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
 | 
				
			||||||
 | 
					- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### API
 | 
					### API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,17 +131,18 @@ Used by the projects below. Feel free to make a PR to add your project to this l
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Questions or want to test on your favorite platform? Join below:
 | 
					Questions or want to test on your favorite platform? Join below:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [Discord][mb-discord]
 | 
				
			||||||
- [Gitter][mb-gitter]
 | 
					- [Gitter][mb-gitter]
 | 
				
			||||||
- [IRC][mb-irc]
 | 
					- [IRC][mb-irc]
 | 
				
			||||||
- [Discord][mb-discord]
 | 
					- [Keybase][mb-keybase]
 | 
				
			||||||
- [Matrix][mb-matrix]
 | 
					- [Matrix][mb-matrix]
 | 
				
			||||||
- [Slack][mb-slack]
 | 
					 | 
				
			||||||
- [Mattermost][mb-mattermost]
 | 
					- [Mattermost][mb-mattermost]
 | 
				
			||||||
- [Rocket.Chat][mb-rocketchat]
 | 
					- [Rocket.Chat][mb-rocketchat]
 | 
				
			||||||
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
 | 
					- [Slack][mb-slack]
 | 
				
			||||||
- [Twitch][mb-twitch]
 | 
					 | 
				
			||||||
- [Zulip][mb-zulip]
 | 
					 | 
				
			||||||
- [Telegram][mb-telegram]
 | 
					- [Telegram][mb-telegram]
 | 
				
			||||||
 | 
					- [Twitch][mb-twitch]
 | 
				
			||||||
 | 
					- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
 | 
				
			||||||
 | 
					- [Zulip][mb-zulip]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Screenshots
 | 
					## Screenshots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -142,14 +152,15 @@ See https://github.com/42wim/matterbridge/wiki
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### Binaries
 | 
					### Binaries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Latest stable release [v1.16.4](https://github.com/42wim/matterbridge/releases/latest)
 | 
					- Latest stable release [v1.18.1](https://github.com/42wim/matterbridge/releases/latest)
 | 
				
			||||||
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
 | 
					- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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.
 | 
					To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Packages
 | 
					### Packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Overview](https://repology.org/metapackage/matterbridge/versions)
 | 
					- [Overview](https://repology.org/metapackage/matterbridge/versions)
 | 
				
			||||||
 | 
					- [snap](https://snapcraft.io/matterbridge)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Building
 | 
					## Building
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -297,6 +308,7 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
 | 
				
			|||||||
- https://kopano.com/blog/matterbridge-bridging-mattermost-chat/
 | 
					- https://kopano.com/blog/matterbridge-bridging-mattermost-chat/
 | 
				
			||||||
- https://www.stitcher.com/s/?eid=52382713
 | 
					- https://www.stitcher.com/s/?eid=52382713
 | 
				
			||||||
- https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
 | 
					- https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
 | 
				
			||||||
 | 
					- https://userlinux.net/mattermost-and-matterbridge.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Thanks
 | 
					## Thanks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -315,30 +327,32 @@ Matterbridge wouldn't exist without these libraries:
 | 
				
			|||||||
- gops - https://github.com/google/gops
 | 
					- gops - https://github.com/google/gops
 | 
				
			||||||
- gozulipbot - https://github.com/ifo/gozulipbot
 | 
					- gozulipbot - https://github.com/ifo/gozulipbot
 | 
				
			||||||
- irc - https://github.com/lrstanley/girc
 | 
					- irc - https://github.com/lrstanley/girc
 | 
				
			||||||
- mattermost - https://github.com/mattermost/mattermost-server
 | 
					- keybase - https://github.com/keybase/go-keybase-chat-bot
 | 
				
			||||||
- matrix - https://github.com/matrix-org/gomatrix
 | 
					- matrix - https://github.com/matrix-org/gomatrix
 | 
				
			||||||
- sshchat - https://github.com/shazow/ssh-chat
 | 
					- mattermost - https://github.com/mattermost/mattermost-server
 | 
				
			||||||
 | 
					- msgraph.go - https://github.com/yaegashi/msgraph.go
 | 
				
			||||||
- slack - https://github.com/nlopes/slack
 | 
					- slack - https://github.com/nlopes/slack
 | 
				
			||||||
 | 
					- sshchat - https://github.com/shazow/ssh-chat
 | 
				
			||||||
- steam - https://github.com/Philipp15b/go-steam
 | 
					- steam - https://github.com/Philipp15b/go-steam
 | 
				
			||||||
- telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
 | 
					- telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
 | 
				
			||||||
- xmpp - https://github.com/mattn/go-xmpp
 | 
					 | 
				
			||||||
- whatsapp - https://github.com/Rhymen/go-whatsapp/
 | 
					 | 
				
			||||||
- zulip - https://github.com/ifo/gozulipbot
 | 
					 | 
				
			||||||
- tengo - https://github.com/d5/tengo
 | 
					- tengo - https://github.com/d5/tengo
 | 
				
			||||||
- keybase - https://github.com/keybase/go-keybase-chat-bot
 | 
					- whatsapp - https://github.com/Rhymen/go-whatsapp/
 | 
				
			||||||
 | 
					- xmpp - https://github.com/mattn/go-xmpp
 | 
				
			||||||
 | 
					- zulip - https://github.com/ifo/gozulipbot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- Links -->
 | 
					<!-- Links -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[mb-discord]: https://discord.gg/AkKPtrQ
 | 
				
			||||||
[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-keybase]: https://keybase.io/team/matterbridge
 | 
				
			||||||
[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-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
 | 
					[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
 | 
				
			||||||
 | 
					[mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7
 | 
				
			||||||
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
 | 
					[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
 | 
				
			||||||
[mb-xmpp]: https://inverse.chat/
 | 
					[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
 | 
				
			||||||
 | 
					[mb-telegram]: https://t.me/Matterbridge
 | 
				
			||||||
[mb-twitch]: https://www.twitch.tv/matterbridge
 | 
					[mb-twitch]: https://www.twitch.tv/matterbridge
 | 
				
			||||||
[mb-whatsapp]: https://www.whatsapp.com/
 | 
					[mb-whatsapp]: https://www.whatsapp.com/
 | 
				
			||||||
[mb-keybase]: https://keybase.io
 | 
					[mb-xmpp]: https://inverse.chat/
 | 
				
			||||||
[mb-zulip]: https://matterbridge.zulipchat.com/register/
 | 
					[mb-zulip]: https://matterbridge.zulipchat.com/register/
 | 
				
			||||||
[mb-telegram]: https://t.me/Matterbridge
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,9 +8,10 @@ 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/gorilla/websocket"
 | 
				
			||||||
	"github.com/labstack/echo/v4"
 | 
						"github.com/labstack/echo/v4"
 | 
				
			||||||
	"github.com/labstack/echo/v4/middleware"
 | 
						"github.com/labstack/echo/v4/middleware"
 | 
				
			||||||
	"github.com/zfjagann/golang-ring"
 | 
						ring "github.com/zfjagann/golang-ring"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type API struct {
 | 
					type API struct {
 | 
				
			||||||
@@ -41,9 +42,17 @@ func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			|||||||
			return key == b.GetString("Token"), nil
 | 
								return key == b.GetString("Token"), nil
 | 
				
			||||||
		}))
 | 
							}))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set RemoteNickFormat to a sane default
 | 
				
			||||||
 | 
						if !b.IsKeySet("RemoteNickFormat") {
 | 
				
			||||||
 | 
							b.Log.Debugln("RemoteNickFormat is unset, defaulting to \"{NICK}\"")
 | 
				
			||||||
 | 
							b.Config.Config.Viper().Set(b.GetConfigKey("RemoteNickFormat"), "{NICK}")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	e.GET("/api/health", b.handleHealthcheck)
 | 
						e.GET("/api/health", b.handleHealthcheck)
 | 
				
			||||||
	e.GET("/api/messages", b.handleMessages)
 | 
						e.GET("/api/messages", b.handleMessages)
 | 
				
			||||||
	e.GET("/api/stream", b.handleStream)
 | 
						e.GET("/api/stream", b.handleStream)
 | 
				
			||||||
 | 
						e.GET("/api/websocket", b.handleWebsocket)
 | 
				
			||||||
	e.POST("/api/message", b.handlePostMessage)
 | 
						e.POST("/api/message", b.handlePostMessage)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		if b.GetString("BindAddress") == "" {
 | 
							if b.GetString("BindAddress") == "" {
 | 
				
			||||||
@@ -106,13 +115,17 @@ func (b *API) handleMessages(c echo.Context) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *API) handleStream(c echo.Context) error {
 | 
					func (b *API) getGreeting() config.Message {
 | 
				
			||||||
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
 | 
						return config.Message{
 | 
				
			||||||
	c.Response().WriteHeader(http.StatusOK)
 | 
					 | 
				
			||||||
	greet := config.Message{
 | 
					 | 
				
			||||||
		Event:     config.EventAPIConnected,
 | 
							Event:     config.EventAPIConnected,
 | 
				
			||||||
		Timestamp: time.Now(),
 | 
							Timestamp: time.Now(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *API) handleStream(c echo.Context) error {
 | 
				
			||||||
 | 
						c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
 | 
				
			||||||
 | 
						c.Response().WriteHeader(http.StatusOK)
 | 
				
			||||||
 | 
						greet := b.getGreeting()
 | 
				
			||||||
	if err := json.NewEncoder(c.Response()).Encode(greet); err != nil {
 | 
						if err := json.NewEncoder(c.Response()).Encode(greet); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -128,3 +141,52 @@ func (b *API) handleStream(c echo.Context) error {
 | 
				
			|||||||
		time.Sleep(200 * time.Millisecond)
 | 
							time.Sleep(200 * time.Millisecond)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *API) handleWebsocketMessage(message config.Message) {
 | 
				
			||||||
 | 
						message.Channel = "api"
 | 
				
			||||||
 | 
						message.Protocol = "api"
 | 
				
			||||||
 | 
						message.Account = b.Account
 | 
				
			||||||
 | 
						message.ID = ""
 | 
				
			||||||
 | 
						message.Timestamp = time.Now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.Log.Debugf("Sending websocket message from %s on %s to gateway", message.Username, "api")
 | 
				
			||||||
 | 
						b.Remote <- message
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *API) writePump(conn *websocket.Conn) {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							msg := b.Messages.Dequeue()
 | 
				
			||||||
 | 
							if msg != nil {
 | 
				
			||||||
 | 
								err := conn.WriteJSON(msg)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *API) readPump(conn *websocket.Conn) {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							message := config.Message{}
 | 
				
			||||||
 | 
							err := conn.ReadJSON(&message)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							b.handleWebsocketMessage(message)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *API) handleWebsocket(c echo.Context) error {
 | 
				
			||||||
 | 
						conn, err := websocket.Upgrade(c.Response().Writer, c.Request(), nil, 1024, 1024)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						greet := b.getGreeting()
 | 
				
			||||||
 | 
						_ = conn.WriteJSON(greet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go b.writePump(conn)
 | 
				
			||||||
 | 
						go b.readPump(conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
@@ -74,6 +75,7 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
 | 
				
			|||||||
	for ID, channel := range channels {
 | 
						for ID, channel := range channels {
 | 
				
			||||||
		if !exists[ID] {
 | 
							if !exists[ID] {
 | 
				
			||||||
			b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
 | 
								b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
 | 
				
			||||||
 | 
								time.Sleep(time.Duration(b.GetInt("JoinDelay")) * time.Millisecond)
 | 
				
			||||||
			err := b.JoinChannel(channel)
 | 
								err := b.JoinChannel(channel)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
@@ -84,8 +86,16 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) GetConfigKey(key string) string {
 | 
				
			||||||
 | 
						return b.Account + "." + key
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bridge) IsKeySet(key string) bool {
 | 
				
			||||||
 | 
						return b.Config.IsKeySet(b.GetConfigKey(key)) || b.Config.IsKeySet("general."+key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bridge) GetBool(key string) bool {
 | 
					func (b *Bridge) GetBool(key string) bool {
 | 
				
			||||||
	val, ok := b.Config.GetBool(b.Account + "." + key)
 | 
						val, ok := b.Config.GetBool(b.GetConfigKey(key))
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		val, _ = b.Config.GetBool("general." + key)
 | 
							val, _ = b.Config.GetBool("general." + key)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -93,7 +103,7 @@ func (b *Bridge) GetBool(key string) bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bridge) GetInt(key string) int {
 | 
					func (b *Bridge) GetInt(key string) int {
 | 
				
			||||||
	val, ok := b.Config.GetInt(b.Account + "." + key)
 | 
						val, ok := b.Config.GetInt(b.GetConfigKey(key))
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		val, _ = b.Config.GetInt("general." + key)
 | 
							val, _ = b.Config.GetInt("general." + key)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -101,7 +111,7 @@ func (b *Bridge) GetInt(key string) int {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bridge) GetString(key string) string {
 | 
					func (b *Bridge) GetString(key string) string {
 | 
				
			||||||
	val, ok := b.Config.GetString(b.Account + "." + key)
 | 
						val, ok := b.Config.GetString(b.GetConfigKey(key))
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		val, _ = b.Config.GetString("general." + key)
 | 
							val, _ = b.Config.GetString("general." + key)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -109,7 +119,7 @@ func (b *Bridge) GetString(key string) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bridge) GetStringSlice(key string) []string {
 | 
					func (b *Bridge) GetStringSlice(key string) []string {
 | 
				
			||||||
	val, ok := b.Config.GetStringSlice(b.Account + "." + key)
 | 
						val, ok := b.Config.GetStringSlice(b.GetConfigKey(key))
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		val, _ = b.Config.GetStringSlice("general." + key)
 | 
							val, _ = b.Config.GetStringSlice("general." + key)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -117,7 +127,7 @@ func (b *Bridge) GetStringSlice(key string) []string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bridge) GetStringSlice2D(key string) [][]string {
 | 
					func (b *Bridge) GetStringSlice2D(key string) [][]string {
 | 
				
			||||||
	val, ok := b.Config.GetStringSlice2D(b.Account + "." + key)
 | 
						val, ok := b.Config.GetStringSlice2D(b.GetConfigKey(key))
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		val, _ = b.Config.GetStringSlice2D("general." + key)
 | 
							val, _ = b.Config.GetStringSlice2D("general." + key)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@ package config
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -76,24 +78,29 @@ 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
 | 
						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
 | 
				
			||||||
 | 
						HTMLDisable            bool   // matrix
 | 
				
			||||||
	IconURL                string // mattermost, slack
 | 
						IconURL                string // mattermost, slack
 | 
				
			||||||
	IgnoreFailureOnStart   bool   // general
 | 
						IgnoreFailureOnStart   bool   // general
 | 
				
			||||||
	IgnoreNicks            string // all protocols
 | 
						IgnoreNicks            string // all protocols
 | 
				
			||||||
	IgnoreMessages         string // all protocols
 | 
						IgnoreMessages         string // all protocols
 | 
				
			||||||
	Jid                    string // xmpp
 | 
						Jid                    string // xmpp
 | 
				
			||||||
 | 
						JoinDelay              string // all protocols
 | 
				
			||||||
	Label                  string // all protocols
 | 
						Label                  string // all protocols
 | 
				
			||||||
	Login                  string // mattermost, matrix
 | 
						Login                  string // mattermost, matrix
 | 
				
			||||||
 | 
						LogFile                string // general
 | 
				
			||||||
	MediaDownloadBlackList []string
 | 
						MediaDownloadBlackList []string
 | 
				
			||||||
	MediaDownloadPath      string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
 | 
						MediaDownloadPath      string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
 | 
				
			||||||
	MediaDownloadSize      int    // all protocols
 | 
						MediaDownloadSize      int    // all protocols
 | 
				
			||||||
	MediaServerDownload    string
 | 
						MediaServerDownload    string
 | 
				
			||||||
	MediaServerUpload      string
 | 
						MediaServerUpload      string
 | 
				
			||||||
 | 
						MediaConvertTgs        string     // telegram
 | 
				
			||||||
	MediaConvertWebPToPNG  bool       // telegram
 | 
						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
 | 
				
			||||||
@@ -124,6 +131,7 @@ type Protocol struct {
 | 
				
			|||||||
	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
 | 
				
			||||||
@@ -131,13 +139,17 @@ type Protocol struct {
 | 
				
			|||||||
	SkipTLSVerify          bool       // IRC, mattermost
 | 
						SkipTLSVerify          bool       // IRC, mattermost
 | 
				
			||||||
	SkipVersionCheck       bool       // mattermost
 | 
						SkipVersionCheck       bool       // mattermost
 | 
				
			||||||
	StripNick              bool       // all protocols
 | 
						StripNick              bool       // all protocols
 | 
				
			||||||
 | 
						StripMarkdown          bool       // irc
 | 
				
			||||||
	SyncTopic              bool       // slack
 | 
						SyncTopic              bool       // slack
 | 
				
			||||||
	TengoModifyMessage     string     // general
 | 
						TengoModifyMessage     string     // general
 | 
				
			||||||
	Team                   string     // mattermost, keybase
 | 
						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
 | 
				
			||||||
@@ -210,6 +222,7 @@ type BridgeValues struct {
 | 
				
			|||||||
type Config interface {
 | 
					type Config interface {
 | 
				
			||||||
	Viper() *viper.Viper
 | 
						Viper() *viper.Viper
 | 
				
			||||||
	BridgeValues() *BridgeValues
 | 
						BridgeValues() *BridgeValues
 | 
				
			||||||
 | 
						IsKeySet(key string) bool
 | 
				
			||||||
	GetBool(key string) (bool, bool)
 | 
						GetBool(key string) (bool, bool)
 | 
				
			||||||
	GetInt(key string) (int, bool)
 | 
						GetInt(key string) (int, bool)
 | 
				
			||||||
	GetString(key string) (string, bool)
 | 
						GetString(key string) (string, bool)
 | 
				
			||||||
@@ -235,7 +248,17 @@ 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.LogFile != "" {
 | 
				
			||||||
 | 
							logfile, err := os.OpenFile(mycfg.cv.General.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								logger.Info("Opening log file ", mycfg.cv.General.LogFile)
 | 
				
			||||||
 | 
								rootLogger.Out = logfile
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								logger.Warn("Failed to open ", mycfg.cv.General.LogFile)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if mycfg.cv.General.MediaDownloadSize == 0 {
 | 
						if mycfg.cv.General.MediaDownloadSize == 0 {
 | 
				
			||||||
		mycfg.cv.General.MediaDownloadSize = 1000000
 | 
							mycfg.cv.General.MediaDownloadSize = 1000000
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -246,14 +269,26 @@ 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()
 | 
				
			||||||
@@ -281,6 +316,12 @@ func (c *config) Viper() *viper.Viper {
 | 
				
			|||||||
	return c.v
 | 
						return c.v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *config) IsKeySet(key string) bool {
 | 
				
			||||||
 | 
						c.RLock()
 | 
				
			||||||
 | 
						defer c.RUnlock()
 | 
				
			||||||
 | 
						return c.v.IsSet(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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()
 | 
				
			||||||
@@ -340,6 +381,11 @@ type TestConfig struct {
 | 
				
			|||||||
	Overrides map[string]interface{}
 | 
						Overrides map[string]interface{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *TestConfig) IsKeySet(key string) bool {
 | 
				
			||||||
 | 
						_, ok := c.Overrides[key]
 | 
				
			||||||
 | 
						return ok || c.Config.IsKeySet(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *TestConfig) GetBool(key string) (bool, bool) {
 | 
					func (c *TestConfig) GetBool(key string) (bool, bool) {
 | 
				
			||||||
	val, ok := c.Overrides[key]
 | 
						val, ok := c.Overrides[key]
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,6 +21,7 @@ type Bdiscord struct {
 | 
				
			|||||||
	c *discordgo.Session
 | 
						c *discordgo.Session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nick            string
 | 
						nick            string
 | 
				
			||||||
 | 
						userID          string
 | 
				
			||||||
	guildID         string
 | 
						guildID         string
 | 
				
			||||||
	webhookID       string
 | 
						webhookID       string
 | 
				
			||||||
	webhookToken    string
 | 
						webhookToken    string
 | 
				
			||||||
@@ -33,6 +34,8 @@ type Bdiscord struct {
 | 
				
			|||||||
	membersMutex  sync.RWMutex
 | 
						membersMutex  sync.RWMutex
 | 
				
			||||||
	userMemberMap map[string]*discordgo.Member
 | 
						userMemberMap map[string]*discordgo.Member
 | 
				
			||||||
	nickMemberMap map[string]*discordgo.Member
 | 
						nickMemberMap map[string]*discordgo.Member
 | 
				
			||||||
 | 
						webhookCache  map[string]string
 | 
				
			||||||
 | 
						webhookMutex  sync.RWMutex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
@@ -40,6 +43,7 @@ func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			|||||||
	b.userMemberMap = make(map[string]*discordgo.Member)
 | 
						b.userMemberMap = make(map[string]*discordgo.Member)
 | 
				
			||||||
	b.nickMemberMap = make(map[string]*discordgo.Member)
 | 
						b.nickMemberMap = make(map[string]*discordgo.Member)
 | 
				
			||||||
	b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
						b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
				
			||||||
 | 
						b.webhookCache = make(map[string]string)
 | 
				
			||||||
	if b.GetString("WebhookURL") != "" {
 | 
						if b.GetString("WebhookURL") != "" {
 | 
				
			||||||
		b.Log.Debug("Configuring Discord Incoming Webhook")
 | 
							b.Log.Debug("Configuring Discord Incoming Webhook")
 | 
				
			||||||
		b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
 | 
							b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
 | 
				
			||||||
@@ -92,6 +96,7 @@ func (b *Bdiscord) Connect() error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1)
 | 
						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 {
 | 
				
			||||||
@@ -114,30 +119,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()
 | 
				
			||||||
@@ -179,6 +191,8 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			|||||||
func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
					func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
				
			||||||
	b.Log.Debugf("=> Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						origMsgID := msg.ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	channelID := b.getChannelID(msg.Channel)
 | 
						channelID := b.getChannelID(msg.Channel)
 | 
				
			||||||
	if channelID == "" {
 | 
						if channelID == "" {
 | 
				
			||||||
		return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
 | 
							return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
 | 
				
			||||||
@@ -215,12 +229,13 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	// Use webhook to send the message
 | 
						// Use webhook to send the message
 | 
				
			||||||
	if wID != "" && msg.Event != config.EventMsgDelete {
 | 
						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.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// If we are editing a message, delete the old message
 | 
							// If we are editing a message, delete the old message
 | 
				
			||||||
		if msg.ID != "" {
 | 
							if msg.ID != "" {
 | 
				
			||||||
 | 
								msg.ID = b.getCacheID(msg.ID)
 | 
				
			||||||
			b.Log.Debugf("Deleting edited webhook message")
 | 
								b.Log.Debugf("Deleting edited webhook message")
 | 
				
			||||||
			err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
								err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
@@ -264,6 +279,8 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		if msg == nil {
 | 
							if msg == nil {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							b.updateCacheID(origMsgID, msg.ID)
 | 
				
			||||||
		return msg.ID, nil
 | 
							return msg.ID, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -274,6 +291,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		if msg.ID == "" {
 | 
							if msg.ID == "" {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							msg.ID = b.getCacheID(msg.ID)
 | 
				
			||||||
		err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
							err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -380,6 +398,19 @@ func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*d
 | 
				
			|||||||
		err error
 | 
							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`.
 | 
						// WebhookParams can have either `Content` or `File`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We can't send empty messages.
 | 
						// We can't send empty messages.
 | 
				
			||||||
@@ -429,3 +460,11 @@ func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*d
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return res, err
 | 
						return res, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bdiscord) findAvatar(m *config.Message) string {
 | 
				
			||||||
 | 
						member, err := b.getGuildMemberByNick(m.Username)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return member.User.AvatarURL("")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ package bdiscord
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					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
 | 
				
			||||||
@@ -36,6 +36,11 @@ func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart)
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore our own typing messages
 | 
				
			||||||
 | 
						if m.UserID == b.userID {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
 | 
						rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
 | 
				
			||||||
	rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
						rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
				
			||||||
	b.Remote <- rmsg
 | 
						b.Remote <- rmsg
 | 
				
			||||||
@@ -90,12 +95,12 @@ 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// set username
 | 
						fromWebhook := m.WebhookID != ""
 | 
				
			||||||
	if !b.GetBool("UseUserName") {
 | 
						if !fromWebhook && !b.GetBool("UseUserName") {
 | 
				
			||||||
		rmsg.Username = b.getNick(m.Author, m.GuildID)
 | 
							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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -103,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)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -119,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
 | 
				
			||||||
@@ -192,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,7 +6,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode"
 | 
						"unicode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/bwmarrin/discordgo"
 | 
						"github.com/matterbridge/discordgo"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string {
 | 
					func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string {
 | 
				
			||||||
@@ -137,6 +137,7 @@ 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]+>")
 | 
				
			||||||
	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 {
 | 
				
			||||||
@@ -182,9 +183,14 @@ func (b *Bdiscord) replaceUserMentions(text string) string {
 | 
				
			|||||||
	return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc)
 | 
						return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func replaceEmotes(text string) string {
 | 
				
			||||||
 | 
						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, "_") {
 | 
						length := len(text)
 | 
				
			||||||
		return text[1 : len(text)-1], true
 | 
						if length > 1 && text[0] == '_' && text[length-1] == '_' {
 | 
				
			||||||
 | 
							return text[1 : length-1], true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return text, false
 | 
						return text, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -203,6 +209,40 @@ func (b *Bdiscord) splitURL(url string) (string, string) {
 | 
				
			|||||||
	return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken]
 | 
						return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getcacheID tries to find a corresponding msgID in the webhook cache.
 | 
				
			||||||
 | 
					// if not found returns the original request.
 | 
				
			||||||
 | 
					func (b *Bdiscord) getCacheID(msgID string) string {
 | 
				
			||||||
 | 
						b.webhookMutex.RLock()
 | 
				
			||||||
 | 
						defer b.webhookMutex.RUnlock()
 | 
				
			||||||
 | 
						for k, v := range b.webhookCache {
 | 
				
			||||||
 | 
							if msgID == k {
 | 
				
			||||||
 | 
								return v
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return msgID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// updateCacheID updates the cache so that the newID takes the place of
 | 
				
			||||||
 | 
					// the original ID. This is used for edit/deletes in combination with webhooks
 | 
				
			||||||
 | 
					// as editing a message via webhook means deleting the message and creating a
 | 
				
			||||||
 | 
					// new message (with a new ID). This ID needs to be set instead of the original ID
 | 
				
			||||||
 | 
					func (b *Bdiscord) updateCacheID(origID, newID string) {
 | 
				
			||||||
 | 
						b.webhookMutex.Lock()
 | 
				
			||||||
 | 
						match := false
 | 
				
			||||||
 | 
						for k, v := range b.webhookCache {
 | 
				
			||||||
 | 
							if v == origID {
 | 
				
			||||||
 | 
								delete(b.webhookCache, k)
 | 
				
			||||||
 | 
								b.webhookCache[origID] = newID
 | 
				
			||||||
 | 
								match = true
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !match && origID != "" {
 | 
				
			||||||
 | 
							b.webhookCache[origID] = newID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.webhookMutex.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func enumerateUsernames(s string) []string {
 | 
					func enumerateUsernames(s string) []string {
 | 
				
			||||||
	onlySpace := true
 | 
						onlySpace := true
 | 
				
			||||||
	for _, r := range s {
 | 
						for _, r := range s {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,10 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"image/png"
 | 
						"image/png"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -180,7 +183,7 @@ func ClipMessage(text string, length int) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ParseMarkdown takes in an input string as markdown and parses it to html
 | 
					// ParseMarkdown takes in an input string as markdown and parses it to html
 | 
				
			||||||
func ParseMarkdown(input string) string {
 | 
					func ParseMarkdown(input string) string {
 | 
				
			||||||
	extensions := parser.HardLineBreak
 | 
						extensions := parser.HardLineBreak | parser.NoIntraEmphasis
 | 
				
			||||||
	markdownParser := parser.NewWithExtensions(extensions)
 | 
						markdownParser := parser.NewWithExtensions(extensions)
 | 
				
			||||||
	renderer := html.NewRenderer(html.RendererOptions{
 | 
						renderer := html.NewRenderer(html.RendererOptions{
 | 
				
			||||||
		Flags: 0,
 | 
							Flags: 0,
 | 
				
			||||||
@@ -192,7 +195,7 @@ func ParseMarkdown(input string) string {
 | 
				
			|||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertWebPToPNG convert input data (which should be WebP format to PNG format)
 | 
					// ConvertWebPToPNG converts input data (which should be WebP format) to PNG format
 | 
				
			||||||
func ConvertWebPToPNG(data *[]byte) error {
 | 
					func ConvertWebPToPNG(data *[]byte) error {
 | 
				
			||||||
	r := bytes.NewReader(*data)
 | 
						r := bytes.NewReader(*data)
 | 
				
			||||||
	m, err := webp.Decode(r)
 | 
						m, err := webp.Decode(r)
 | 
				
			||||||
@@ -207,3 +210,49 @@ func ConvertWebPToPNG(data *[]byte) error {
 | 
				
			|||||||
	*data = w.Bytes()
 | 
						*data = w.Bytes()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CanConvertTgsToX Checks whether the external command necessary for ConvertTgsToX works.
 | 
				
			||||||
 | 
					func CanConvertTgsToX() error {
 | 
				
			||||||
 | 
						// We depend on the fact that `lottie_convert.py --help` has exit status 0.
 | 
				
			||||||
 | 
						// Hyrum's Law predicted this, and Murphy's Law predicts that this will break eventually.
 | 
				
			||||||
 | 
						// However, there is no alternative like `lottie_convert.py --is-properly-installed`
 | 
				
			||||||
 | 
						cmd := exec.Command("lottie_convert.py", "--help")
 | 
				
			||||||
 | 
						return cmd.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConvertTgsToWebP convert input data (which should be tgs format) to WebP format
 | 
				
			||||||
 | 
					// This relies on an external command, which is ugly, but works.
 | 
				
			||||||
 | 
					func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error {
 | 
				
			||||||
 | 
						// lottie can't handle input from a pipe, so write to a temporary file:
 | 
				
			||||||
 | 
						tmpFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-*.tgs")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tmpFileName := tmpFile.Name()
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if removeErr := os.Remove(tmpFileName); removeErr != nil {
 | 
				
			||||||
 | 
								logger.Errorf("Could not delete temporary file %s: %v", tmpFileName, removeErr)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, writeErr := tmpFile.Write(*data); writeErr != nil {
 | 
				
			||||||
 | 
							return writeErr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Must close before calling lottie to avoid data races:
 | 
				
			||||||
 | 
						if closeErr := tmpFile.Close(); closeErr != nil {
 | 
				
			||||||
 | 
							return closeErr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Call lottie to transform:
 | 
				
			||||||
 | 
						cmd := exec.Command("lottie_convert.py", "--input-format", "lottie", "--output-format", outputFormat, tmpFileName, "/dev/stdout")
 | 
				
			||||||
 | 
						cmd.Stderr = nil
 | 
				
			||||||
 | 
						// NB: lottie writes progress into to stderr in all cases.
 | 
				
			||||||
 | 
						stdout, stderr := cmd.Output()
 | 
				
			||||||
 | 
						if stderr != nil {
 | 
				
			||||||
 | 
							// 'stderr' already contains some parts of Stderr, because it was set to 'nil'.
 | 
				
			||||||
 | 
							return stderr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*data = stdout
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,8 @@ 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/dfordsoft/golib/ic"
 | 
					 | 
				
			||||||
	"github.com/lrstanley/girc"
 | 
						"github.com/lrstanley/girc"
 | 
				
			||||||
 | 
						"github.com/missdeer/golib/ic"
 | 
				
			||||||
	"github.com/paulrosania/go-charset/charset"
 | 
						"github.com/paulrosania/go-charset/charset"
 | 
				
			||||||
	"github.com/saintfish/chardet"
 | 
						"github.com/saintfish/chardet"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,12 +54,12 @@ func (b *Birc) handleFiles(msg *config.Message) bool {
 | 
				
			|||||||
	for _, f := range msg.Extra["file"] {
 | 
						for _, f := range msg.Extra["file"] {
 | 
				
			||||||
		fi := f.(config.FileInfo)
 | 
							fi := f.(config.FileInfo)
 | 
				
			||||||
		if fi.Comment != "" {
 | 
							if fi.Comment != "" {
 | 
				
			||||||
			msg.Text += fi.Comment + ": "
 | 
								msg.Text += fi.Comment + " : "
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if fi.URL != "" {
 | 
							if fi.URL != "" {
 | 
				
			||||||
			msg.Text = fi.URL
 | 
								msg.Text = fi.URL
 | 
				
			||||||
			if fi.Comment != "" {
 | 
								if fi.Comment != "" {
 | 
				
			||||||
				msg.Text = fi.Comment + ": " + fi.URL
 | 
									msg.Text = fi.Comment + " : " + fi.URL
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
							b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import (
 | 
				
			|||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/lrstanley/girc"
 | 
						"github.com/lrstanley/girc"
 | 
				
			||||||
 | 
						stripmd "github.com/writeas/go-strip-markdown"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We need to import the 'data' package as an implicit dependency.
 | 
						// We need to import the 'data' package as an implicit dependency.
 | 
				
			||||||
	// See: https://godoc.org/github.com/paulrosania/go-charset/charset
 | 
						// See: https://godoc.org/github.com/paulrosania/go-charset/charset
 | 
				
			||||||
@@ -156,6 +157,10 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var msgLines []string
 | 
						var msgLines []string
 | 
				
			||||||
 | 
						if b.GetBool("StripMarkdown") {
 | 
				
			||||||
 | 
							msg.Text = stripmd.Strip(msg.Text)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if b.GetBool("MessageSplit") {
 | 
						if b.GetBool("MessageSplit") {
 | 
				
			||||||
		msgLines = helper.GetSubLines(msg.Text, b.MessageLength)
 | 
							msgLines = helper.GetSubLines(msg.Text, b.MessageLength)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@@ -167,12 +172,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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -205,7 +206,7 @@ func (b *Birc) doSend() {
 | 
				
			|||||||
	for msg := range b.Local {
 | 
						for msg := range b.Local {
 | 
				
			||||||
		<-throttle.C
 | 
							<-throttle.C
 | 
				
			||||||
		username := msg.Username
 | 
							username := msg.Username
 | 
				
			||||||
		if b.GetBool("Colornicks") {
 | 
							if b.GetBool("Colornicks") && len(username) > 1 {
 | 
				
			||||||
			checksum := crc32.ChecksumIEEE([]byte(msg.Username))
 | 
								checksum := crc32.ChecksumIEEE([]byte(msg.Username))
 | 
				
			||||||
			colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
 | 
								colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
 | 
				
			||||||
			username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
 | 
								username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
 | 
				
			||||||
@@ -249,6 +250,8 @@ func (b *Birc) getClient() (*girc.Client, error) {
 | 
				
			|||||||
		SSL:        b.GetBool("UseTLS"),
 | 
							SSL:        b.GetBool("UseTLS"),
 | 
				
			||||||
		TLSConfig:  &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
 | 
							TLSConfig:  &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
 | 
				
			||||||
		PingDelay:  time.Minute,
 | 
							PingDelay:  time.Minute,
 | 
				
			||||||
 | 
							// skip gIRC internal rate limiting, since we have our own throttling
 | 
				
			||||||
 | 
							AllowFlood: true,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	return i, nil
 | 
						return i, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/keybase/go-keybase-chat-bot/kbchat"
 | 
						"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bkeybase) handleKeybase() {
 | 
					func (b *Bkeybase) handleKeybase() {
 | 
				
			||||||
@@ -20,7 +20,7 @@ func (b *Bkeybase) handleKeybase() {
 | 
				
			|||||||
				b.Log.Errorf("failed to read message: %s", err.Error())
 | 
									b.Log.Errorf("failed to read message: %s", err.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if msg.Message.Content.Type != "text" {
 | 
								if msg.Message.Content.TypeName != "text" {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,7 +34,7 @@ func (b *Bkeybase) handleKeybase() {
 | 
				
			|||||||
	}()
 | 
						}()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bkeybase) handleMessage(msg kbchat.Message) {
 | 
					func (b *Bkeybase) handleMessage(msg chat1.MsgSummary) {
 | 
				
			||||||
	b.Log.Debugf("== Receiving event: %#v", msg)
 | 
						b.Log.Debugf("== Receiving event: %#v", msg)
 | 
				
			||||||
	if msg.Channel.TopicName != b.channel || msg.Channel.Name != b.team {
 | 
						if msg.Channel.TopicName != b.channel || msg.Channel.Name != b.team {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -45,10 +45,10 @@ func (b *Bkeybase) handleMessage(msg kbchat.Message) {
 | 
				
			|||||||
		// TODO download avatar
 | 
							// TODO download avatar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Create our message
 | 
							// Create our message
 | 
				
			||||||
		rmsg := config.Message{Username: msg.Sender.Username, Text: msg.Content.Text.Body, UserID: msg.Sender.Uid, Channel: msg.Channel.TopicName, ID: strconv.Itoa(msg.MsgID), Account: b.Account}
 | 
							rmsg := config.Message{Username: msg.Sender.Username, Text: msg.Content.Text.Body, UserID: string(msg.Sender.Uid), Channel: msg.Channel.TopicName, ID: strconv.Itoa(int(msg.Id)), Account: b.Account}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Text must be a string
 | 
							// Text must be a string
 | 
				
			||||||
		if msg.Content.Type != "text" {
 | 
							if msg.Content.TypeName != "text" {
 | 
				
			||||||
			b.Log.Errorf("message is not text")
 | 
								b.Log.Errorf("message is not text")
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,16 +90,17 @@ func (b *Bkeybase) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
				return "", err
 | 
									return "", err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			_, _ = b.kbc.SendAttachmentByTeam(b.team, fpath, fcaption, &b.channel)
 | 
								_, _ = b.kbc.SendAttachmentByTeam(b.team, &b.channel, fpath, fcaption)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Send regular message
 | 
						// Send regular message
 | 
				
			||||||
	resp, err := b.kbc.SendMessageByTeamName(b.team, msg.Username+msg.Text, &b.channel)
 | 
						text := msg.Username + msg.Text
 | 
				
			||||||
 | 
						resp, err := b.kbc.SendMessageByTeamName(b.team, &b.channel, text)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return strconv.Itoa(resp.Result.MsgID), err
 | 
						return strconv.Itoa(int(*resp.Result.MessageID)), err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,14 @@ package bmatrix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
	"mime"
 | 
						"mime"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge"
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
@@ -20,13 +22,21 @@ type Bmatrix struct {
 | 
				
			|||||||
	UserID  string
 | 
						UserID  string
 | 
				
			||||||
	RoomMap map[string]string
 | 
						RoomMap map[string]string
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
	htmlTag *regexp.Regexp
 | 
						htmlTag            *regexp.Regexp
 | 
				
			||||||
 | 
						htmlReplacementTag *regexp.Regexp
 | 
				
			||||||
	*bridge.Config
 | 
						*bridge.Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpError struct {
 | 
				
			||||||
 | 
						Errcode      string `json:"errcode"`
 | 
				
			||||||
 | 
						Err          string `json:"error"`
 | 
				
			||||||
 | 
						RetryAfterMs int    `json:"retry_after_ms"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
	b := &Bmatrix{Config: cfg}
 | 
						b := &Bmatrix{Config: cfg}
 | 
				
			||||||
	b.htmlTag = regexp.MustCompile("</.*?>")
 | 
						b.htmlTag = regexp.MustCompile("</.*?>")
 | 
				
			||||||
 | 
						b.htmlReplacementTag = regexp.MustCompile("<[^>]*>")
 | 
				
			||||||
	b.RoomMap = make(map[string]string)
 | 
						b.RoomMap = make(map[string]string)
 | 
				
			||||||
	return b
 | 
						return b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -58,14 +68,25 @@ func (b *Bmatrix) Disconnect() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
 | 
					func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
 | 
					retry:
 | 
				
			||||||
	resp, err := b.mc.JoinRoom(channel.Name, "", nil)
 | 
						resp, err := b.mc.JoinRoom(channel.Name, "", nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							httpErr := handleError(err)
 | 
				
			||||||
 | 
							if httpErr.Errcode == "M_LIMIT_EXCEEDED" {
 | 
				
			||||||
 | 
								b.Log.Infof("getting ratelimited by matrix, sleeping approx %d seconds before joining %s", httpErr.RetryAfterMs/1000, channel.Name)
 | 
				
			||||||
 | 
								time.Sleep((time.Duration(httpErr.RetryAfterMs) * time.Millisecond))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								goto retry
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.Lock()
 | 
						b.Lock()
 | 
				
			||||||
	b.RoomMap[resp.RoomID] = channel.Name
 | 
						b.RoomMap[resp.RoomID] = channel.Name
 | 
				
			||||||
	b.Unlock()
 | 
						b.Unlock()
 | 
				
			||||||
	return err
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
					func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
				
			||||||
@@ -124,13 +145,28 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		return resp.EventID, err
 | 
							return resp.EventID, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	username := html.EscapeString(msg.Username)
 | 
						if b.GetBool("HTMLDisable") {
 | 
				
			||||||
 | 
							resp, err := b.mc.SendText(channel, msg.Username+msg.Text)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return resp.EventID, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var username string
 | 
				
			||||||
 | 
						var plainUsername string
 | 
				
			||||||
	// check if we have a </tag>. if we have, we don't escape HTML. #696
 | 
						// check if we have a </tag>. if we have, we don't escape HTML. #696
 | 
				
			||||||
	if b.htmlTag.MatchString(msg.Username) {
 | 
						if b.htmlTag.MatchString(msg.Username) {
 | 
				
			||||||
		username = msg.Username
 | 
							username = msg.Username
 | 
				
			||||||
 | 
							// remove the HTML formatting for beautiful push messages #1188
 | 
				
			||||||
 | 
							plainUsername = b.htmlReplacementTag.ReplaceAllString(msg.Username, "")
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							username = html.EscapeString(msg.Username)
 | 
				
			||||||
 | 
							plainUsername = msg.Username
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Post normal message with HTML support (eg riot.im)
 | 
						// Post normal message with HTML support (eg riot.im)
 | 
				
			||||||
	resp, err := b.mc.SendHTML(channel, msg.Username+msg.Text, username+helper.ParseMarkdown(msg.Text))
 | 
						resp, err := b.mc.SendHTML(channel, plainUsername+msg.Text, username+helper.ParseMarkdown(msg.Text))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -172,10 +208,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 {
 | 
				
			||||||
@@ -358,3 +399,36 @@ 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/")
 | 
				
			||||||
 | 
						if url != "" {
 | 
				
			||||||
 | 
							url += "?width=37&height=37&method=crop"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return url
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleError(err error) *httpError {
 | 
				
			||||||
 | 
						mErr, ok := err.(matrix.HTTPError)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return &httpError{
 | 
				
			||||||
 | 
								Err: "not a HTTPError",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var httpErr httpError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := json.Unmarshal(mErr.Contents, &httpErr); err != nil {
 | 
				
			||||||
 | 
							return &httpError{
 | 
				
			||||||
 | 
								Err: "unmarshal failed",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &httpErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ 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/matterclient"
 | 
						"github.com/42wim/matterbridge/matterclient"
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// handleDownloadAvatar downloads the avatar of userid from channel
 | 
					// handleDownloadAvatar downloads the avatar of userid from channel
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterclient"
 | 
						"github.com/42wim/matterbridge/matterclient"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterhook"
 | 
						"github.com/42wim/matterbridge/matterhook"
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bmattermost) doConnectWebhookBind() error {
 | 
					func (b *Bmattermost) doConnectWebhookBind() error {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										101
									
								
								bridge/msteams/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								bridge/msteams/handler.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					package bmsteams
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msgraph "github.com/yaegashi/msgraph.go/beta"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) findFile(weburl string) (string, error) {
 | 
				
			||||||
 | 
						itemRB, err := b.gc.GetDriveItemByURL(b.ctx, weburl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						itemRB.Workbook().Worksheets()
 | 
				
			||||||
 | 
						b.gc.Workbooks()
 | 
				
			||||||
 | 
						item, err := itemRB.Request().Get(b.ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if url, ok := item.GetAdditionalData("@microsoft.graph.downloadUrl"); ok {
 | 
				
			||||||
 | 
							return url.(string), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleDownloadFile handles file download
 | 
				
			||||||
 | 
					func (b *Bmsteams) handleDownloadFile(rmsg *config.Message, filename, weburl string) error {
 | 
				
			||||||
 | 
						realURL, err := b.findFile(weburl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Actually download the file.
 | 
				
			||||||
 | 
						data, err := helper.DownloadFile(realURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("download %s failed %#v", weburl, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If a comment is attached to the file(s) it is in the 'Text' field of the teams messge event
 | 
				
			||||||
 | 
						// and should be added as comment to only one of the files. We reset the 'Text' field to ensure
 | 
				
			||||||
 | 
						// that the comment is not duplicated.
 | 
				
			||||||
 | 
						comment := rmsg.Text
 | 
				
			||||||
 | 
						rmsg.Text = ""
 | 
				
			||||||
 | 
						helper.HandleDownloadData(b.Log, rmsg, filename, comment, weburl, data, b.General)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) handleAttachments(rmsg *config.Message, msg msgraph.ChatMessage) {
 | 
				
			||||||
 | 
						for _, a := range msg.Attachments {
 | 
				
			||||||
 | 
							//remove the attachment tags from the text
 | 
				
			||||||
 | 
							rmsg.Text = attachRE.ReplaceAllString(rmsg.Text, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//handle a code snippet (code block)
 | 
				
			||||||
 | 
							if *a.ContentType == "application/vnd.microsoft.card.codesnippet" {
 | 
				
			||||||
 | 
								b.handleCodeSnippet(rmsg, a)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//handle the download
 | 
				
			||||||
 | 
							err := b.handleDownloadFile(rmsg, *a.Name, *a.ContentURL)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("download of %s failed: %s", *a.Name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AttachContent struct {
 | 
				
			||||||
 | 
						Language       string `json:"language"`
 | 
				
			||||||
 | 
						CodeSnippetURL string `json:"codeSnippetUrl"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) handleCodeSnippet(rmsg *config.Message, attach msgraph.ChatMessageAttachment) {
 | 
				
			||||||
 | 
						var content AttachContent
 | 
				
			||||||
 | 
						err := json.Unmarshal([]byte(*attach.Content), &content)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("unmarshal codesnippet failed: %s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s := strings.Split(content.CodeSnippetURL, "/")
 | 
				
			||||||
 | 
						if len(s) != 13 {
 | 
				
			||||||
 | 
							b.Log.Errorf("codesnippetUrl has unexpected size: %s", content.CodeSnippetURL)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp, err := b.gc.Teams().Request().Client().Get(content.CodeSnippetURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("retrieving snippet content failed:%s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
						res, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("reading snippet data failed: %s", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rmsg.Text = rmsg.Text + "\n```" + content.Language + "\n" + string(res) + "\n```\n"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										224
									
								
								bridge/msteams/msteams.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								bridge/msteams/msteams.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					package bmsteams
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/davecgh/go-spew/spew"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mattn/godown"
 | 
				
			||||||
 | 
						msgraph "github.com/yaegashi/msgraph.go/beta"
 | 
				
			||||||
 | 
						"github.com/yaegashi/msgraph.go/msauth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var defaultScopes = []string{"openid", "profile", "offline_access", "Group.Read.All", "Group.ReadWrite.All"}
 | 
				
			||||||
 | 
					var attachRE = regexp.MustCompile(`<attachment id=.*?attachment>`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Bmsteams struct {
 | 
				
			||||||
 | 
						gc    *msgraph.GraphServiceRequestBuilder
 | 
				
			||||||
 | 
						ctx   context.Context
 | 
				
			||||||
 | 
						botID string
 | 
				
			||||||
 | 
						*bridge.Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
 | 
						return &Bmsteams{Config: cfg}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) Connect() error {
 | 
				
			||||||
 | 
						tokenCachePath := b.GetString("sessionFile")
 | 
				
			||||||
 | 
						if tokenCachePath == "" {
 | 
				
			||||||
 | 
							tokenCachePath = "msteams_session.json"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
						m := msauth.NewManager()
 | 
				
			||||||
 | 
						m.LoadFile(tokenCachePath) //nolint:errcheck
 | 
				
			||||||
 | 
						ts, err := m.DeviceAuthorizationGrant(ctx, b.GetString("TenantID"), b.GetString("ClientID"), defaultScopes, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = m.SaveFile(tokenCachePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("Couldn't save sessionfile in %s: %s", tokenCachePath, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// make file readable only for matterbridge user
 | 
				
			||||||
 | 
						err = os.Chmod(tokenCachePath, 0600)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("Couldn't change permissions for %s: %s", tokenCachePath, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						httpClient := oauth2.NewClient(ctx, ts)
 | 
				
			||||||
 | 
						graphClient := msgraph.NewClient(httpClient)
 | 
				
			||||||
 | 
						b.gc = graphClient
 | 
				
			||||||
 | 
						b.ctx = ctx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = b.setBotID()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Log.Info("Connection succeeded")
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) Disconnect() error {
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
 | 
						go func(name string) {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								err := b.poll(name)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									b.Log.Errorf("polling failed for %s: %s. retrying in 5 seconds", name, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								time.Sleep(time.Second * 5)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}(channel.Name)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) Send(msg config.Message) (string, error) {
 | 
				
			||||||
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
						if msg.ParentID != "" && msg.ParentID != "msg-parent-not-found" {
 | 
				
			||||||
 | 
							return b.sendReply(msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if msg.ParentID == "msg-parent-not-found" {
 | 
				
			||||||
 | 
							msg.ParentID = ""
 | 
				
			||||||
 | 
							msg.Text = fmt.Sprintf("[thread]: %s", msg.Text)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().Request()
 | 
				
			||||||
 | 
						text := msg.Username + msg.Text
 | 
				
			||||||
 | 
						content := &msgraph.ItemBody{Content: &text}
 | 
				
			||||||
 | 
						rmsg := &msgraph.ChatMessage{Body: content}
 | 
				
			||||||
 | 
						res, err := ct.Add(b.ctx, rmsg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return *res.ID, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) sendReply(msg config.Message) (string, error) {
 | 
				
			||||||
 | 
						ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().ID(msg.ParentID).Replies().Request()
 | 
				
			||||||
 | 
						// Handle prefix hint for unthreaded messages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						text := msg.Username + msg.Text
 | 
				
			||||||
 | 
						content := &msgraph.ItemBody{Content: &text}
 | 
				
			||||||
 | 
						rmsg := &msgraph.ChatMessage{Body: content}
 | 
				
			||||||
 | 
						res, err := ct.Add(b.ctx, rmsg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return *res.ID, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) {
 | 
				
			||||||
 | 
						ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(channel).Messages().Request()
 | 
				
			||||||
 | 
						rct, err := ct.Get(b.ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Log.Debugf("got %#v messages", len(rct))
 | 
				
			||||||
 | 
						return rct, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//nolint:gocognit
 | 
				
			||||||
 | 
					func (b *Bmsteams) poll(channelName string) error {
 | 
				
			||||||
 | 
						msgmap := make(map[string]time.Time)
 | 
				
			||||||
 | 
						b.Log.Debug("getting initial messages")
 | 
				
			||||||
 | 
						res, err := b.getMessages(channelName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, msg := range res {
 | 
				
			||||||
 | 
							msgmap[*msg.ID] = *msg.CreatedDateTime
 | 
				
			||||||
 | 
							if msg.LastModifiedDateTime != nil {
 | 
				
			||||||
 | 
								msgmap[*msg.ID] = *msg.LastModifiedDateTime
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						time.Sleep(time.Second * 5)
 | 
				
			||||||
 | 
						b.Log.Debug("polling for messages")
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							res, err := b.getMessages(channelName)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for i := len(res) - 1; i >= 0; i-- {
 | 
				
			||||||
 | 
								msg := res[i]
 | 
				
			||||||
 | 
								if mtime, ok := msgmap[*msg.ID]; ok {
 | 
				
			||||||
 | 
									if mtime == *msg.CreatedDateTime && msg.LastModifiedDateTime == nil {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if msg.LastModifiedDateTime != nil && mtime == *msg.LastModifiedDateTime {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if b.GetBool("debug") {
 | 
				
			||||||
 | 
									b.Log.Debug("Msg dump: ", spew.Sdump(msg))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// skip non-user message for now.
 | 
				
			||||||
 | 
								if msg.From.User == nil {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if *msg.From.User.ID == b.botID {
 | 
				
			||||||
 | 
									b.Log.Debug("skipping own message")
 | 
				
			||||||
 | 
									msgmap[*msg.ID] = *msg.CreatedDateTime
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								msgmap[*msg.ID] = *msg.CreatedDateTime
 | 
				
			||||||
 | 
								if msg.LastModifiedDateTime != nil {
 | 
				
			||||||
 | 
									msgmap[*msg.ID] = *msg.LastModifiedDateTime
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								b.Log.Debugf("<= Sending message from %s on %s to gateway", *msg.From.User.DisplayName, b.Account)
 | 
				
			||||||
 | 
								text := b.convertToMD(*msg.Body.Content)
 | 
				
			||||||
 | 
								rmsg := config.Message{
 | 
				
			||||||
 | 
									Username: *msg.From.User.DisplayName,
 | 
				
			||||||
 | 
									Text:     text,
 | 
				
			||||||
 | 
									Channel:  channelName,
 | 
				
			||||||
 | 
									Account:  b.Account,
 | 
				
			||||||
 | 
									Avatar:   "",
 | 
				
			||||||
 | 
									UserID:   *msg.From.User.ID,
 | 
				
			||||||
 | 
									ID:       *msg.ID,
 | 
				
			||||||
 | 
									Extra:    make(map[string][]interface{}),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								b.handleAttachments(&rmsg, msg)
 | 
				
			||||||
 | 
								b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
 | 
								b.Remote <- rmsg
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							time.Sleep(time.Second * 5)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) setBotID() error {
 | 
				
			||||||
 | 
						req := b.gc.Me().Request()
 | 
				
			||||||
 | 
						r, err := req.Get(b.ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.botID = *r.ID
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bmsteams) convertToMD(text string) string {
 | 
				
			||||||
 | 
						if !strings.Contains(text, "<div>") {
 | 
				
			||||||
 | 
							return text
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var sb strings.Builder
 | 
				
			||||||
 | 
						err := godown.Convert(&sb, strings.NewReader(text), nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("Couldn't convert message to markdown %s", text)
 | 
				
			||||||
 | 
							return text
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sb.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										114
									
								
								bridge/nctalk/nctalk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								bridge/nctalk/nctalk.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
				
			|||||||
 | 
					package nctalk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						talk "gomod.garykim.dev/nc-talk"
 | 
				
			||||||
 | 
						"gomod.garykim.dev/nc-talk/ocs"
 | 
				
			||||||
 | 
						"gomod.garykim.dev/nc-talk/room"
 | 
				
			||||||
 | 
						"gomod.garykim.dev/nc-talk/user"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Btalk struct {
 | 
				
			||||||
 | 
						user  *user.TalkUser
 | 
				
			||||||
 | 
						rooms []Broom
 | 
				
			||||||
 | 
						*bridge.Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
 | 
						return &Btalk{Config: cfg}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Broom struct {
 | 
				
			||||||
 | 
						room      *room.TalkRoom
 | 
				
			||||||
 | 
						ctx       context.Context
 | 
				
			||||||
 | 
						ctxCancel context.CancelFunc
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btalk) Connect() error {
 | 
				
			||||||
 | 
						b.Log.Info("Connecting")
 | 
				
			||||||
 | 
						b.user = talk.NewUser(b.GetString("Server"), b.GetString("Login"), b.GetString("Password"))
 | 
				
			||||||
 | 
						_, err := b.user.Capabilities()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Error("Cannot Connect")
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Log.Info("Connected")
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btalk) Disconnect() error {
 | 
				
			||||||
 | 
						for _, r := range b.rooms {
 | 
				
			||||||
 | 
							r.ctxCancel()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
 | 
				
			||||||
 | 
						newRoom := Broom{
 | 
				
			||||||
 | 
							room: talk.NewRoom(b.user, channel.Name),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						newRoom.ctx, newRoom.ctxCancel = context.WithCancel(context.Background())
 | 
				
			||||||
 | 
						c, err := newRoom.room.ReceiveMessages(newRoom.ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.rooms = append(b.rooms, newRoom)
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for msg := range c {
 | 
				
			||||||
 | 
								// ignore messages that are one of the following
 | 
				
			||||||
 | 
								// * not a message from a user
 | 
				
			||||||
 | 
								// * from ourselves
 | 
				
			||||||
 | 
								if msg.MessageType != ocs.MessageComment || msg.ActorID == b.user.User {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								remoteMessage := config.Message{
 | 
				
			||||||
 | 
									Text:     msg.Message,
 | 
				
			||||||
 | 
									Channel:  newRoom.room.Token,
 | 
				
			||||||
 | 
									Username: msg.ActorDisplayName,
 | 
				
			||||||
 | 
									UserID:   msg.ActorID,
 | 
				
			||||||
 | 
									Account:  b.Account,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// It is possible for the ID to not be set on older versions of Talk so we only set it if
 | 
				
			||||||
 | 
								// the ID is not blank
 | 
				
			||||||
 | 
								if msg.ID != 0 {
 | 
				
			||||||
 | 
									remoteMessage.ID = strconv.Itoa(msg.ID)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								b.Log.Debugf("<= Message is %#v", remoteMessage)
 | 
				
			||||||
 | 
								b.Remote <- remoteMessage
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btalk) Send(msg config.Message) (string, error) {
 | 
				
			||||||
 | 
						r := b.getRoom(msg.Channel)
 | 
				
			||||||
 | 
						if r == nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("Could not find room for %v", msg.Channel)
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Talk currently only supports sending normal messages
 | 
				
			||||||
 | 
						if msg.Event != "" {
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sentMessage, err := r.room.SendMessage(msg.Username + msg.Text)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err)
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strconv.Itoa(sentMessage.ID), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btalk) getRoom(token string) *Broom {
 | 
				
			||||||
 | 
						for _, r := range b.rooms {
 | 
				
			||||||
 | 
							if r.room.Token == token {
 | 
				
			||||||
 | 
								return &r
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@ package brocketchat
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/matterbridge/Rocket.Chat.Go.SDK/models"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Brocketchat) handleRocket() {
 | 
					func (b *Brocketchat) handleRocket() {
 | 
				
			||||||
@@ -38,6 +39,23 @@ func (b *Brocketchat) handleRocketHook(messages chan *config.Message) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Brocketchat) handleStatusEvent(ev models.Message, rmsg *config.Message) bool {
 | 
				
			||||||
 | 
						switch ev.Type {
 | 
				
			||||||
 | 
						case "":
 | 
				
			||||||
 | 
							// this is a normal message, no processing needed
 | 
				
			||||||
 | 
							// return true so the message is not dropped
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						case sUserJoined, sUserLeft:
 | 
				
			||||||
 | 
							rmsg.Event = config.EventJoinLeave
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						case sRoomChangedTopic:
 | 
				
			||||||
 | 
							rmsg.Event = config.EventTopicChange
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Log.Debugf("Dropping message with unknown type: %s", ev.Type)
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
 | 
					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
 | 
							// skip messages with same ID, apparently messages get duplicated for an unknown reason
 | 
				
			||||||
@@ -59,7 +77,12 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
 | 
				
			|||||||
			UserID:   message.User.ID,
 | 
								UserID:   message.User.ID,
 | 
				
			||||||
			ID:       message.ID,
 | 
								ID:       message.ID,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		messages <- rmsg
 | 
					
 | 
				
			||||||
 | 
							// handleStatusEvent returns false if the message should be dropped
 | 
				
			||||||
 | 
							// in that case it is probably some modification to the channel we do not want to relay
 | 
				
			||||||
 | 
							if b.handleStatusEvent(m, rmsg) {
 | 
				
			||||||
 | 
								messages <- rmsg
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,12 @@ type Brocketchat struct {
 | 
				
			|||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						sUserJoined       = "uj"
 | 
				
			||||||
 | 
						sUserLeft         = "ul"
 | 
				
			||||||
 | 
						sRoomChangedTopic = "room_changed_topic"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
	newCache, err := lru.New(100)
 | 
						newCache, err := lru.New(100)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,22 @@
 | 
				
			|||||||
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) != "" && b.GetString(tokenConfig) == "" {
 | 
				
			||||||
		b.Log.Debugf("Choosing webhooks based receiving")
 | 
							b.Log.Debugf("Choosing webhooks based receiving")
 | 
				
			||||||
		go b.handleMatterHook(messages)
 | 
							go b.handleMatterHook(messages)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@@ -53,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
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -87,7 +93,7 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
 | 
				
			|||||||
			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.users.populateUser(ev.User)
 | 
								b.users.populateUser(ev.User)
 | 
				
			||||||
		case *slack.HelloEvent, *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)
 | 
				
			||||||
@@ -124,11 +130,11 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Skip any messages that we made ourselves or from 'slackbot' (see #527).
 | 
						// Check for our callback ID
 | 
				
			||||||
	if ev.Username == sSlackBotUser ||
 | 
						hasOurCallbackID := false
 | 
				
			||||||
		(b.rtm != nil && ev.Username == b.si.User.Name) ||
 | 
						if len(ev.Blocks.BlockSet) == 1 {
 | 
				
			||||||
		(len(ev.Attachments) > 0 && ev.Attachments[0].CallbackID == "matterbridge_"+b.uuid) {
 | 
							block, ok := ev.Blocks.BlockSet[0].(*slack.SectionBlock)
 | 
				
			||||||
		return true
 | 
							hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ev.SubMessage != nil {
 | 
						if ev.SubMessage != nil {
 | 
				
			||||||
@@ -143,6 +149,16 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
 | 
				
			|||||||
		if ev.SubType == "message_replied" && ev.Hidden {
 | 
							if ev.SubType == "message_replied" && ev.Hidden {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if len(ev.SubMessage.Blocks.BlockSet) == 1 {
 | 
				
			||||||
 | 
								block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock)
 | 
				
			||||||
 | 
								hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Skip any messages that we made ourselves or from 'slackbot' (see #527).
 | 
				
			||||||
 | 
						if ev.Username == sSlackBotUser ||
 | 
				
			||||||
 | 
							(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(ev.Files) > 0 {
 | 
						if len(ev.Files) > 0 {
 | 
				
			||||||
@@ -270,6 +286,9 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
 | 
					func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
 | 
				
			||||||
 | 
						if ev.User == b.si.User.ID {
 | 
				
			||||||
 | 
							return nil, ErrEventIgnored
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	channelInfo, err := b.channels.getChannelByID(ev.Channel)
 | 
						channelInfo, err := b.channels.getChannelByID(ev.Channel)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,8 +7,8 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/nlopes/slack"
 | 
					 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/slack-go/slack"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// populateReceivedMessage shapes the initial Matterbridge message that we will forward to the
 | 
					// populateReceivedMessage shapes the initial Matterbridge message that we will forward to the
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge"
 | 
						"github.com/42wim/matterbridge/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,8 +13,8 @@ import (
 | 
				
			|||||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
	"github.com/42wim/matterbridge/matterhook"
 | 
						"github.com/42wim/matterbridge/matterhook"
 | 
				
			||||||
	lru "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 {
 | 
				
			||||||
@@ -64,6 +64,7 @@ const (
 | 
				
			|||||||
	editSuffixConfig      = "EditSuffix"
 | 
						editSuffixConfig      = "EditSuffix"
 | 
				
			||||||
	iconURLConfig         = "iconurl"
 | 
						iconURLConfig         = "iconurl"
 | 
				
			||||||
	noSendJoinConfig      = "nosendjoinpart"
 | 
						noSendJoinConfig      = "nosendjoinpart"
 | 
				
			||||||
 | 
						messageLength         = 3000
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
@@ -194,6 +195,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
		b.Log.Debugf("=> Receiving %#v", msg)
 | 
							b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg.Text = helper.ClipMessage(msg.Text, messageLength)
 | 
				
			||||||
	msg.Text = b.replaceCodeFence(msg.Text)
 | 
						msg.Text = b.replaceCodeFence(msg.Text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Make a action /me of the message
 | 
						// Make a action /me of the message
 | 
				
			||||||
@@ -202,7 +204,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Use webhook to send the message
 | 
						// Use webhook to send the message
 | 
				
			||||||
	if b.GetString(outgoingWebhookConfig) != "" {
 | 
						if b.GetString(outgoingWebhookConfig) != "" && b.GetString(tokenConfig) == "" {
 | 
				
			||||||
		return "", b.sendWebhook(msg)
 | 
							return "", b.sendWebhook(msg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return b.sendRTM(msg)
 | 
						return b.sendRTM(msg)
 | 
				
			||||||
@@ -408,7 +410,6 @@ 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
 | 
				
			||||||
@@ -427,11 +428,6 @@ 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),
 | 
					 | 
				
			||||||
		slack.MsgOptionEnableLinkUnfurl(),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...)
 | 
							_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
@@ -497,8 +493,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)
 | 
				
			||||||
@@ -509,6 +503,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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,8 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
	"github.com/nlopes/slack"
 | 
					 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/slack-go/slack"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const minimumRefreshInterval = 10 * time.Second
 | 
					const minimumRefreshInterval = 10 * time.Second
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,22 +39,32 @@ func (b *Btelegram) handleGroups(rmsg *config.Message, message *tgbotapi.Message
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// handleForwarded handles forwarded messages
 | 
					// handleForwarded handles forwarded messages
 | 
				
			||||||
func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Message) {
 | 
					func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Message) {
 | 
				
			||||||
	if message.ForwardFrom != nil {
 | 
						if message.ForwardDate == 0 {
 | 
				
			||||||
		usernameForward := ""
 | 
							return
 | 
				
			||||||
		if b.GetBool("UseFirstName") {
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if message.ForwardFrom == nil {
 | 
				
			||||||
 | 
							rmsg.Text = "Forwarded from " + unknownUser + ": " + rmsg.Text
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						usernameForward := ""
 | 
				
			||||||
 | 
						if b.GetBool("UseFirstName") {
 | 
				
			||||||
 | 
							usernameForward = message.ForwardFrom.FirstName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if usernameForward == "" {
 | 
				
			||||||
 | 
							usernameForward = message.ForwardFrom.UserName
 | 
				
			||||||
 | 
							if usernameForward == "" {
 | 
				
			||||||
			usernameForward = message.ForwardFrom.FirstName
 | 
								usernameForward = message.ForwardFrom.FirstName
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if usernameForward == "" {
 | 
					 | 
				
			||||||
			usernameForward = message.ForwardFrom.UserName
 | 
					 | 
				
			||||||
			if usernameForward == "" {
 | 
					 | 
				
			||||||
				usernameForward = message.ForwardFrom.FirstName
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if usernameForward == "" {
 | 
					 | 
				
			||||||
			usernameForward = unknownUser
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if usernameForward == "" {
 | 
				
			||||||
 | 
							usernameForward = unknownUser
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// handleQuoting handles quoting of previous messages
 | 
					// handleQuoting handles quoting of previous messages
 | 
				
			||||||
@@ -95,7 +105,7 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// only download avatars if we have a place to upload them (configured mediaserver)
 | 
							// 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)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -207,6 +217,46 @@ func (b *Btelegram) handleDownloadAvatar(userid int, channel string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btelegram) maybeConvertTgs(name *string, data *[]byte) {
 | 
				
			||||||
 | 
						var format string
 | 
				
			||||||
 | 
						switch b.GetString("MediaConvertTgs") {
 | 
				
			||||||
 | 
						case FormatWebp:
 | 
				
			||||||
 | 
							b.Log.Debugf("Tgs to WebP conversion enabled, converting %v", name)
 | 
				
			||||||
 | 
							format = FormatWebp
 | 
				
			||||||
 | 
						case FormatPng:
 | 
				
			||||||
 | 
							// The WebP to PNG converter can't handle animated webp files yet,
 | 
				
			||||||
 | 
							// and I'm not going to write a path for x/image/webp.
 | 
				
			||||||
 | 
							// The error message would be:
 | 
				
			||||||
 | 
							//     conversion failed: webp: non-Alpha VP8X is not implemented
 | 
				
			||||||
 | 
							// So instead, we tell lottie to directly go to PNG.
 | 
				
			||||||
 | 
							b.Log.Debugf("Tgs to PNG conversion enabled, converting %v", name)
 | 
				
			||||||
 | 
							format = FormatPng
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// Otherwise, no conversion was requested. Trying to run the usual webp
 | 
				
			||||||
 | 
							// converter would fail, because '.tgs.webp' is actually a gzipped JSON
 | 
				
			||||||
 | 
							// file, and has nothing to do with WebP.
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err := helper.ConvertTgsToX(data, format, b.Log)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("conversion failed: %v", err)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							*name = strings.Replace(*name, "tgs.webp", format, 1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Btelegram) maybeConvertWebp(name *string, data *[]byte) {
 | 
				
			||||||
 | 
						if b.GetBool("MediaConvertWebPToPNG") {
 | 
				
			||||||
 | 
							b.Log.Debugf("WebP to PNG conversion enabled, converting %v", name)
 | 
				
			||||||
 | 
							err := helper.ConvertWebPToPNG(data)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Errorf("conversion failed: %v", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								*name = strings.Replace(*name, ".webp", ".png", 1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// handleDownloadFile handles file download
 | 
					// handleDownloadFile handles file download
 | 
				
			||||||
func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error {
 | 
					func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error {
 | 
				
			||||||
	size := 0
 | 
						size := 0
 | 
				
			||||||
@@ -254,15 +304,13 @@ 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)
 | 
						if strings.HasSuffix(name, ".tgs.webp") {
 | 
				
			||||||
		err := helper.ConvertWebPToPNG(data)
 | 
							b.maybeConvertTgs(&name, data)
 | 
				
			||||||
		if err != nil {
 | 
						} else if strings.HasSuffix(name, ".webp") {
 | 
				
			||||||
			b.Log.Errorf("conversion failed: %s", err)
 | 
							b.maybeConvertWebp(&name, data)
 | 
				
			||||||
		} 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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -312,6 +360,9 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error
 | 
				
			|||||||
	case "Markdown":
 | 
						case "Markdown":
 | 
				
			||||||
		b.Log.Debug("Using mode markdown")
 | 
							b.Log.Debug("Using mode markdown")
 | 
				
			||||||
		m.ParseMode = tgbotapi.ModeMarkdown
 | 
							m.ParseMode = tgbotapi.ModeMarkdown
 | 
				
			||||||
 | 
						case MarkdownV2:
 | 
				
			||||||
 | 
							b.Log.Debug("Using mode MarkdownV2")
 | 
				
			||||||
 | 
							m.ParseMode = MarkdownV2
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
 | 
						if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
 | 
				
			||||||
		b.Log.Debug("Using mode HTML - nick only")
 | 
							b.Log.Debug("Using mode HTML - nick only")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package btelegram
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,6 +16,9 @@ const (
 | 
				
			|||||||
	unknownUser = "unknown"
 | 
						unknownUser = "unknown"
 | 
				
			||||||
	HTMLFormat  = "HTML"
 | 
						HTMLFormat  = "HTML"
 | 
				
			||||||
	HTMLNick    = "htmlnick"
 | 
						HTMLNick    = "htmlnick"
 | 
				
			||||||
 | 
						MarkdownV2  = "MarkdownV2"
 | 
				
			||||||
 | 
						FormatPng   = "png"
 | 
				
			||||||
 | 
						FormatWebp  = "webp"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Btelegram struct {
 | 
					type Btelegram struct {
 | 
				
			||||||
@@ -24,6 +28,16 @@ type Btelegram struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
 | 
						tgsConvertFormat := cfg.GetString("MediaConvertTgs")
 | 
				
			||||||
 | 
						if tgsConvertFormat != "" {
 | 
				
			||||||
 | 
							err := helper.CanConvertTgsToX()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but lottie does not appear to work:\n%#v", tgsConvertFormat, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if tgsConvertFormat != FormatPng && tgsConvertFormat != FormatWebp {
 | 
				
			||||||
 | 
								log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but only '%s' and '%s' are supported.", FormatPng, FormatWebp, tgsConvertFormat)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return &Btelegram{Config: cfg, avatarMap: make(map[string]string)}
 | 
						return &Btelegram{Config: cfg, avatarMap: make(map[string]string)}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,6 +140,10 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, er
 | 
				
			|||||||
		b.Log.Debug("Using mode markdown")
 | 
							b.Log.Debug("Using mode markdown")
 | 
				
			||||||
		m.ParseMode = tgbotapi.ModeMarkdown
 | 
							m.ParseMode = tgbotapi.ModeMarkdown
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if b.GetString("MessageFormat") == MarkdownV2 {
 | 
				
			||||||
 | 
							b.Log.Debug("Using mode MarkdownV2")
 | 
				
			||||||
 | 
							m.ParseMode = MarkdownV2
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
 | 
						if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
 | 
				
			||||||
		b.Log.Debug("Using mode HTML - nick only")
 | 
							b.Log.Debug("Using mode HTML - nick only")
 | 
				
			||||||
		m.Text = username + html.EscapeString(text)
 | 
							m.Text = username + html.EscapeString(text)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ 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/Rhymen/go-whatsapp"
 | 
						"github.com/Rhymen/go-whatsapp"
 | 
				
			||||||
 | 
						"github.com/jpillora/backoff"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@@ -22,10 +23,43 @@ Check:
 | 
				
			|||||||
// HandleError received from WhatsApp
 | 
					// HandleError received from WhatsApp
 | 
				
			||||||
func (b *Bwhatsapp) HandleError(err error) {
 | 
					func (b *Bwhatsapp) HandleError(err error) {
 | 
				
			||||||
	// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
 | 
						// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
 | 
				
			||||||
	if strings.Contains(err.Error(), "error processing data: received invalid data") {
 | 
						// ignore tag 174 errors. https://github.com/42wim/matterbridge/issues/1094
 | 
				
			||||||
 | 
						if strings.Contains(err.Error(), "error processing data: received invalid data") || strings.Contains(err.Error(), "invalid string with tag 174") {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
 | 
					
 | 
				
			||||||
 | 
						switch err.(type) {
 | 
				
			||||||
 | 
						case *whatsapp.ErrConnectionClosed, *whatsapp.ErrConnectionFailed:
 | 
				
			||||||
 | 
							b.reconnect(err)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							switch err {
 | 
				
			||||||
 | 
							case whatsapp.ErrConnectionTimeout:
 | 
				
			||||||
 | 
								b.reconnect(err)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								b.Log.Errorf("%v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bwhatsapp) reconnect(err error) {
 | 
				
			||||||
 | 
						bf := &backoff.Backoff{
 | 
				
			||||||
 | 
							Min:    time.Second,
 | 
				
			||||||
 | 
							Max:    5 * time.Minute,
 | 
				
			||||||
 | 
							Jitter: true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							d := bf.Duration()
 | 
				
			||||||
 | 
							b.Log.Errorf("Connection failed, underlying error: %v", err)
 | 
				
			||||||
 | 
							b.Log.Infof("Waiting %s...", d)
 | 
				
			||||||
 | 
							time.Sleep(d)
 | 
				
			||||||
 | 
							b.Log.Info("Reconnecting...")
 | 
				
			||||||
 | 
							err := b.conn.Restore()
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								bf.Reset()
 | 
				
			||||||
 | 
								b.startedAt = uint64(time.Now().Unix())
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HandleTextMessage sent from WhatsApp, relay it to the brige
 | 
					// HandleTextMessage sent from WhatsApp, relay it to the brige
 | 
				
			||||||
@@ -44,7 +78,9 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
 | 
				
			|||||||
	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
 | 
							if message.Info.Source != nil {
 | 
				
			||||||
 | 
								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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,8 +80,33 @@ func (b *Bwhatsapp) getSenderName(senderJid string) string {
 | 
				
			|||||||
		// if user is not in phone contacts
 | 
							// if user is not in phone contacts
 | 
				
			||||||
		// it is the most obvious scenario unless you sync your phone contacts with some remote updated source
 | 
							// it is the most obvious scenario unless you sync your phone contacts with some remote updated source
 | 
				
			||||||
		// users can change it in their WhatsApp settings -> profile -> click on Avatar
 | 
							// users can change it in their WhatsApp settings -> profile -> click on Avatar
 | 
				
			||||||
		return sender.Notify
 | 
							if sender.Notify != "" {
 | 
				
			||||||
 | 
								return sender.Notify
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if sender.Short != "" {
 | 
				
			||||||
 | 
								return sender.Short
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// try to reload this contact
 | 
				
			||||||
 | 
						_, err := b.conn.Contacts()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							b.Log.Errorf("error on update of contacts: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if contact, exists := b.conn.Store.Contacts[senderJid]; exists {
 | 
				
			||||||
 | 
							// Add it to the user map
 | 
				
			||||||
 | 
							b.users[senderJid] = contact
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if contact.Name != "" {
 | 
				
			||||||
 | 
								return contact.Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// if user is not in phone contacts
 | 
				
			||||||
 | 
							// same as above
 | 
				
			||||||
 | 
							return contact.Notify
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,6 +67,7 @@ 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, 2080)
 | 
				
			||||||
	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())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								bridge/xmpp/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								bridge/xmpp/handler.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					package bxmpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/helper"
 | 
				
			||||||
 | 
						"github.com/matterbridge/go-xmpp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleDownloadAvatar downloads the avatar of userid from channel
 | 
				
			||||||
 | 
					// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
 | 
				
			||||||
 | 
					// logs an error message if it fails
 | 
				
			||||||
 | 
					func (b *Bxmpp) handleDownloadAvatar(avatar xmpp.AvatarData) {
 | 
				
			||||||
 | 
						rmsg := config.Message{
 | 
				
			||||||
 | 
							Username: "system",
 | 
				
			||||||
 | 
							Text:     "avatar",
 | 
				
			||||||
 | 
							Channel:  b.parseChannel(avatar.From),
 | 
				
			||||||
 | 
							Account:  b.Account,
 | 
				
			||||||
 | 
							UserID:   avatar.From,
 | 
				
			||||||
 | 
							Event:    config.EventAvatarDownload,
 | 
				
			||||||
 | 
							Extra:    make(map[string][]interface{}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := b.avatarMap[avatar.From]; !ok {
 | 
				
			||||||
 | 
							b.Log.Debugf("Avatar.From: %s", avatar.From)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := helper.HandleDownloadSize(b.Log, &rmsg, avatar.From+".png", int64(len(avatar.Data)), b.General)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								b.Log.Error(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							helper.HandleDownloadData(b.Log, &rmsg, avatar.From+".png", rmsg.Text, "", &avatar.Data, b.General)
 | 
				
			||||||
 | 
							b.Log.Debugf("Avatar download complete")
 | 
				
			||||||
 | 
							b.Remote <- rmsg
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								bridge/xmpp/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								bridge/xmpp/helpers.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package bxmpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAvatar constructs a URL for a given user-avatar if it is available in the cache.
 | 
				
			||||||
 | 
					func getAvatar(av map[string]string, userid string, general *config.Protocol) string {
 | 
				
			||||||
 | 
						if hash, ok := av[userid]; ok {
 | 
				
			||||||
 | 
							// NOTE: This does not happen in bridge/helper/helper.go but messes up XMPP
 | 
				
			||||||
 | 
							id := pathRegex.ReplaceAllString(userid, "_")
 | 
				
			||||||
 | 
							return general.MediaServerDownload + "/" + hash + "/" + id + ".png"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Bxmpp) cacheAvatar(msg *config.Message) string {
 | 
				
			||||||
 | 
						fi := msg.Extra["file"][0].(config.FileInfo)
 | 
				
			||||||
 | 
						/* if we have a sha we have successfully uploaded the file to the media server,
 | 
				
			||||||
 | 
						so we can now cache the sha */
 | 
				
			||||||
 | 
						if fi.SHA != "" {
 | 
				
			||||||
 | 
							b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
 | 
				
			||||||
 | 
							b.avatarMap[msg.UserID] = fi.SHA
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -23,12 +23,17 @@ type Bxmpp struct {
 | 
				
			|||||||
	xmppMap   map[string]string
 | 
						xmppMap   map[string]string
 | 
				
			||||||
	connected bool
 | 
						connected bool
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avatarAvailability map[string]bool
 | 
				
			||||||
 | 
						avatarMap          map[string]string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
					func New(cfg *bridge.Config) bridge.Bridger {
 | 
				
			||||||
	return &Bxmpp{
 | 
						return &Bxmpp{
 | 
				
			||||||
		Config:  cfg,
 | 
							Config:             cfg,
 | 
				
			||||||
		xmppMap: make(map[string]string),
 | 
							xmppMap:            make(map[string]string),
 | 
				
			||||||
 | 
							avatarAvailability: make(map[string]bool),
 | 
				
			||||||
 | 
							avatarMap:          make(map[string]string),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,8 +72,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | 
				
			|||||||
	if msg.Event == config.EventMsgDelete {
 | 
						if msg.Event == config.EventMsgDelete {
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b.Log.Debugf("=> Receiving %#v", msg)
 | 
						b.Log.Debugf("=> Receiving %#v", msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if msg.Event == config.EventAvatarDownload {
 | 
				
			||||||
 | 
							return b.cacheAvatar(&msg), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a action /me of the message, prepend the username with it.
 | 
				
			||||||
 | 
						// https://xmpp.org/extensions/xep-0245.html
 | 
				
			||||||
 | 
						if msg.Event == config.EventUserAction {
 | 
				
			||||||
 | 
							msg.Username = "/me " + msg.Username
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
 | 
						// 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) {
 | 
				
			||||||
@@ -114,6 +130,9 @@ func (b *Bxmpp) createXMPP() error {
 | 
				
			|||||||
		ServerName:         strings.Split(b.GetString("Jid"), "@")[1],
 | 
							ServerName:         strings.Split(b.GetString("Jid"), "@")[1],
 | 
				
			||||||
		InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
 | 
							InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						xmpp.DebugWriter = b.Log.Writer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	options := xmpp.Options{
 | 
						options := xmpp.Options{
 | 
				
			||||||
		Host:                         b.GetString("Server"),
 | 
							Host:                         b.GetString("Server"),
 | 
				
			||||||
		User:                         b.GetString("Jid"),
 | 
							User:                         b.GetString("Jid"),
 | 
				
			||||||
@@ -122,7 +141,6 @@ func (b *Bxmpp) createXMPP() error {
 | 
				
			|||||||
		StartTLS:                     true,
 | 
							StartTLS:                     true,
 | 
				
			||||||
		TLSConfig:                    tc,
 | 
							TLSConfig:                    tc,
 | 
				
			||||||
		Debug:                        b.GetBool("debug"),
 | 
							Debug:                        b.GetBool("debug"),
 | 
				
			||||||
		Logger:                       b.Log.Writer(),
 | 
					 | 
				
			||||||
		Session:                      true,
 | 
							Session:                      true,
 | 
				
			||||||
		Status:                       "",
 | 
							Status:                       "",
 | 
				
			||||||
		StatusMessage:                "",
 | 
							StatusMessage:                "",
 | 
				
			||||||
@@ -228,6 +246,16 @@ func (b *Bxmpp) handleXMPP() error {
 | 
				
			|||||||
					event = config.EventTopicChange
 | 
										event = config.EventTopicChange
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									available, sok := b.avatarAvailability[v.Remote]
 | 
				
			||||||
 | 
									avatar := ""
 | 
				
			||||||
 | 
									if !sok {
 | 
				
			||||||
 | 
										b.Log.Debugf("Requesting avatar data")
 | 
				
			||||||
 | 
										b.avatarAvailability[v.Remote] = false
 | 
				
			||||||
 | 
										b.xc.AvatarRequestData(v.Remote)
 | 
				
			||||||
 | 
									} else if available {
 | 
				
			||||||
 | 
										avatar = getAvatar(b.avatarMap, v.Remote, b.General)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				msgID := v.ID
 | 
									msgID := v.ID
 | 
				
			||||||
				if v.ReplaceID != "" {
 | 
									if v.ReplaceID != "" {
 | 
				
			||||||
					msgID = v.ReplaceID
 | 
										msgID = v.ReplaceID
 | 
				
			||||||
@@ -237,6 +265,7 @@ func (b *Bxmpp) handleXMPP() error {
 | 
				
			|||||||
					Text:     v.Text,
 | 
										Text:     v.Text,
 | 
				
			||||||
					Channel:  b.parseChannel(v.Remote),
 | 
										Channel:  b.parseChannel(v.Remote),
 | 
				
			||||||
					Account:  b.Account,
 | 
										Account:  b.Account,
 | 
				
			||||||
 | 
										Avatar:   avatar,
 | 
				
			||||||
					UserID:   v.Remote,
 | 
										UserID:   v.Remote,
 | 
				
			||||||
					ID:       msgID,
 | 
										ID:       msgID,
 | 
				
			||||||
					Event:    event,
 | 
										Event:    event,
 | 
				
			||||||
@@ -253,6 +282,10 @@ func (b *Bxmpp) handleXMPP() error {
 | 
				
			|||||||
				b.Log.Debugf("<= Message is %#v", rmsg)
 | 
									b.Log.Debugf("<= Message is %#v", rmsg)
 | 
				
			||||||
				b.Remote <- rmsg
 | 
									b.Remote <- rmsg
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							case xmpp.AvatarData:
 | 
				
			||||||
 | 
								b.handleDownloadAvatar(v)
 | 
				
			||||||
 | 
								b.avatarAvailability[v.From] = true
 | 
				
			||||||
 | 
								b.Log.Debugf("Avatar for %s is now available", v.From)
 | 
				
			||||||
		case xmpp.Presence:
 | 
							case xmpp.Presence:
 | 
				
			||||||
			// Do nothing.
 | 
								// Do nothing.
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										170
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,173 @@
 | 
				
			|||||||
 | 
					# v1.18.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- telegram: Support Telegram animated stickers (tgs) format (#1173). See https://github.com/42wim/matterbridge/wiki/Settings#mediaConverttgs for more info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- matrix: Remove HTML formatting for push messages (#1188) (#1189)
 | 
				
			||||||
 | 
					- mattermost: Use mattermost v5 module (#1192)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- whatsapp: Handle panic in whatsapp. Fixes #1180 (#1184)
 | 
				
			||||||
 | 
					- nctalk: Fix Nextcloud Talk connection failure (#1179)
 | 
				
			||||||
 | 
					- matrix: Sleep when ratelimited on joins (matrix). Fixes #1201 (#1206)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@42wim, @BenWiederhake, @Dellle, @gary-kim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.18.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- nctalk: new protocol added. Add Nextcloud Talk support #1167
 | 
				
			||||||
 | 
					- general: Add an option to log into a file rather than stdout (#1168)
 | 
				
			||||||
 | 
					- api: Add websocket to API (#970)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- telegram: Fix MarkdownV2 support in Telegram (#1169)
 | 
				
			||||||
 | 
					- whatsapp: Reload user information when a new contact is detected (whatsapp) (#1160)
 | 
				
			||||||
 | 
					- api: Add sane RemoteNickFormat default for API (#1157)
 | 
				
			||||||
 | 
					- irc: Skip gIRC built-in rate limiting (irc) (#1164)
 | 
				
			||||||
 | 
					- irc: Only colour IRC nicks if there is one. (#1161)
 | 
				
			||||||
 | 
					- docker: Combine runs to one layer (#1151)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- general: Update dependencies for 1.18.0 release (#1175)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Discord users are encouraged to upgrade, this release works with the move to the discord.com domain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@42wim, @jlu5, @qaisjp, @TheHolyRoger, @SuperSandro2000, @gary-kim, @z3bra, @greenx, @haykam821, @nathanaelhoun
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.17.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- irc: Add StripMarkdown option (irc). (#1145)
 | 
				
			||||||
 | 
					- general: Increase debug logging with function,file and linenumber (#1147)
 | 
				
			||||||
 | 
					- general: Update Dockerfile so inotify works (#1148)
 | 
				
			||||||
 | 
					- matrix: Add an option to disable sending HTML to matrix. Fixes #1022 (#1135)
 | 
				
			||||||
 | 
					- xmpp: Implement xep-0245 (xmpp). Closes #1137 (#1144)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- discord: Fix #1120: replaceAction "_" crash (discord) (#1121)
 | 
				
			||||||
 | 
					- discord: Fix #1049: missing space before embeds (discord) (#1124)
 | 
				
			||||||
 | 
					- discord: Fix webhook EventUserAction messages being skipped (discord) (#1133)
 | 
				
			||||||
 | 
					- matrix: Avoid creating invalid url when the user doesn't have an avatar (matrix) (#1130)
 | 
				
			||||||
 | 
					- msteams: Ignore non-user messages (msteams). Fixes #1141 (#1149)
 | 
				
			||||||
 | 
					- slack: Do not use webhooks when token is configured (slack) (fixes #1123) (#1134)
 | 
				
			||||||
 | 
					- telegram: Fix forward from hidden users (telegram). Closes #1131 (#1143)
 | 
				
			||||||
 | 
					- xmpp: Prevent re-requesting avatar data (xmpp) (#1117)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@qaisjp, @xnaas, @42wim, @Polynomdivision, @tfve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.17.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- general: Lowercase account names. Fixes #1108 (#1110)
 | 
				
			||||||
 | 
					- msteams: Remove panics and retry polling on failure (msteams). Fixes #1104 (#1105
 | 
				
			||||||
 | 
					- whatsapp: Update Rhymen/go-whatsapp. Fixes #1107 (#1109) (make whatsapp working again)
 | 
				
			||||||
 | 
					- discord: Add an ID cache (discord). Fixes #1106 (#1111) (fix delete/edits with webhooks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.17.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- xmpp: Implement User Avatar spoofing of XMPP users #1090
 | 
				
			||||||
 | 
					- rocketchat: Relay Joins/Topic changes in RocketChat bridge (#1085)
 | 
				
			||||||
 | 
					- irc: Add JoinDelay option (irc). Fixes #1084 (#1098)
 | 
				
			||||||
 | 
					- slack: Clip too long messages on 3000 length (slack). Fixes #1081 (#1102)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- general: Fix the behavior of ShowTopicChange and SyncTopic (#1086)
 | 
				
			||||||
 | 
					- slack: Prevent image/message looping (slack). Fixes #1088 (#1096)
 | 
				
			||||||
 | 
					- whatsapp: Ignore non-critical errors (whatsapp). Fixes #1094 (#1100)
 | 
				
			||||||
 | 
					- irc: Add extra space before colon in attachments (irc). Fixes #1089 (#1101)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@42wim, @ldruschk, @qaisjp, @Polynomdivision
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.17.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- slack: Update vendor slack-go/slack (#1068)
 | 
				
			||||||
 | 
					- general: Update vendor d5/tengo (#1066)
 | 
				
			||||||
 | 
					- general: Clarify terminology used in mapping group chat IDs to channels in config (#1079)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- whatsapp: Update Rhymen/go-whatsapp vendor and whatsapp version (#1078). Fixes Media upload #1074
 | 
				
			||||||
 | 
					- whatsapp: Reset start timestamp on reconnect (whatsapp). Fixes #1059 (#1064)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@42wim, @jheiselman
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.17.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- docker: Remove build dependencies from final image (multistage build) #1057
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- general: Don't transmit typing events from ourselves #1056
 | 
				
			||||||
 | 
					- general: Add support for build tags #1054
 | 
				
			||||||
 | 
					- discord: Strip extra info from emotes (discord) #1052
 | 
				
			||||||
 | 
					- msteams: fix macos build: Update vendor yaegashi/msgraph.go to v0.1.2 #1036
 | 
				
			||||||
 | 
					- whatsapp: Update client version whatsapp. Fixes #1061 #1062
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@awigen, @qaisjp, @42wim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.17.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## New features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- msteams: new protocol added. Add initial Microsoft Teams support #967
 | 
				
			||||||
 | 
					  See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup for a complete walkthrough
 | 
				
			||||||
 | 
					- discord: Add ability to procure avatars from the destination bridge #1000
 | 
				
			||||||
 | 
					- matrix: Add support for avatars from matrix. #1007
 | 
				
			||||||
 | 
					- general: support JSON and YAML config formats #1045
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- discord: Check only bridged channels for PermManageWebhooks #1001
 | 
				
			||||||
 | 
					- irc: Be less lossy when throttling IRC messages #1004
 | 
				
			||||||
 | 
					- keybase: updated library #1002, #1019
 | 
				
			||||||
 | 
					- matrix: Rebase gomatrix vendor with upstream #1006
 | 
				
			||||||
 | 
					- slack: Use upstream slack-go/slack again #1018
 | 
				
			||||||
 | 
					- slack: Ignore ConnectingEvent #1041
 | 
				
			||||||
 | 
					- slack: use blocks not attachments #1048
 | 
				
			||||||
 | 
					- sshchat: Update vendor shazow/ssh-chat #1029
 | 
				
			||||||
 | 
					- telegram: added markdownv2 mode for telegram #1037
 | 
				
			||||||
 | 
					- whatsapp: Implement basic reconnect (whatsapp). Fixes #987 #1003
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Bugfix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- discord: Fix webhook permission checks sometimes failing #1043
 | 
				
			||||||
 | 
					- discord: Fix #1027: warning when handling inbound webhooks #1044
 | 
				
			||||||
 | 
					- discord: Fix duplicate separator on empty description/url (discord) #1035
 | 
				
			||||||
 | 
					- matrix: Fix issue with underscores in links #999
 | 
				
			||||||
 | 
					- slack: Fix #1039: messages sent to Slack being synced back #1046
 | 
				
			||||||
 | 
					- telegram: Make avatars download work with mediaserverdownload (telegram). Fixes #920 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This release couldn't exist without the following contributors:
 | 
				
			||||||
 | 
					@qaisjp, @jakubgs, @burner1024, @notpushkin, @MartijnBraam, @42wim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# v1.16.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Fix version bump
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# v1.16.4
 | 
					# v1.16.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## New features
 | 
					## New features
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					 | 
				
			||||||
set -u -e -x -o pipefail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
go version | grep go1.13 || exit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VERSION=$(git describe --tags)
 | 
					 | 
				
			||||||
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=linux GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux-amd64
 | 
					 | 
				
			||||||
GOOS=linux GOARCH=arm go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux-arm
 | 
					 | 
				
			||||||
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-darwin-amd64
 | 
					 | 
				
			||||||
cd ci
 | 
					 | 
				
			||||||
cat > deploy.json <<EOF
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    "package": {
 | 
					 | 
				
			||||||
        "name": "Matterbridge",
 | 
					 | 
				
			||||||
        "repo": "nightly",
 | 
					 | 
				
			||||||
        "subject": "42wim"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "version": {
 | 
					 | 
				
			||||||
        "name": "$VERSION"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "files":
 | 
					 | 
				
			||||||
        [
 | 
					 | 
				
			||||||
        {"includePattern": "ci/binaries/(.*)", "uploadPattern":"\$1"}
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
    "publish": true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
EOF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										17
									
								
								ci/lint.sh
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								ci/lint.sh
									
									
									
									
									
								
							@@ -1,17 +0,0 @@
 | 
				
			|||||||
#!/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
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								ci/test.sh
									
									
									
									
									
								
							@@ -1,17 +0,0 @@
 | 
				
			|||||||
#!/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
 | 
					 | 
				
			||||||
							
								
								
									
										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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bnctalk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bnctalk.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !nonctalk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						btalk "github.com/42wim/matterbridge/bridge/nctalk"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["nctalk"] = btalk.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,45 +2,9 @@ package bridgemap
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge"
 | 
						"github.com/42wim/matterbridge/bridge"
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/api"
 | 
					 | 
				
			||||||
	bdiscord "github.com/42wim/matterbridge/bridge/discord"
 | 
					 | 
				
			||||||
	bgitter "github.com/42wim/matterbridge/bridge/gitter"
 | 
					 | 
				
			||||||
	birc "github.com/42wim/matterbridge/bridge/irc"
 | 
					 | 
				
			||||||
	bkeybase "github.com/42wim/matterbridge/bridge/keybase"
 | 
					 | 
				
			||||||
	bmatrix "github.com/42wim/matterbridge/bridge/matrix"
 | 
					 | 
				
			||||||
	bmattermost "github.com/42wim/matterbridge/bridge/mattermost"
 | 
					 | 
				
			||||||
	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
 | 
					 | 
				
			||||||
	bslack "github.com/42wim/matterbridge/bridge/slack"
 | 
					 | 
				
			||||||
	bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
 | 
					 | 
				
			||||||
	bsteam "github.com/42wim/matterbridge/bridge/steam"
 | 
					 | 
				
			||||||
	btelegram "github.com/42wim/matterbridge/bridge/telegram"
 | 
					 | 
				
			||||||
	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
 | 
					 | 
				
			||||||
	bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
 | 
					 | 
				
			||||||
	bzulip "github.com/42wim/matterbridge/bridge/zulip"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	FullMap = map[string]bridge.Factory{
 | 
						FullMap           = map[string]bridge.Factory{}
 | 
				
			||||||
		"api":          api.New,
 | 
						UserTypingSupport = map[string]struct{}{}
 | 
				
			||||||
		"discord":      bdiscord.New,
 | 
					 | 
				
			||||||
		"gitter":       bgitter.New,
 | 
					 | 
				
			||||||
		"irc":          birc.New,
 | 
					 | 
				
			||||||
		"mattermost":   bmattermost.New,
 | 
					 | 
				
			||||||
		"matrix":       bmatrix.New,
 | 
					 | 
				
			||||||
		"rocketchat":   brocketchat.New,
 | 
					 | 
				
			||||||
		"slack-legacy": bslack.NewLegacy,
 | 
					 | 
				
			||||||
		"slack":        bslack.New,
 | 
					 | 
				
			||||||
		"sshchat":      bsshchat.New,
 | 
					 | 
				
			||||||
		"steam":        bsteam.New,
 | 
					 | 
				
			||||||
		"telegram":     btelegram.New,
 | 
					 | 
				
			||||||
		"whatsapp":     bwhatsapp.New,
 | 
					 | 
				
			||||||
		"xmpp":         bxmpp.New,
 | 
					 | 
				
			||||||
		"zulip":        bzulip.New,
 | 
					 | 
				
			||||||
		"keybase":      bkeybase.New,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	UserTypingSupport = map[string]struct{}{
 | 
					 | 
				
			||||||
		"slack":   {},
 | 
					 | 
				
			||||||
		"discord": {},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !norocketchat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["rocketchat"] = brocketchat.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					// +build !noslack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						bslack "github.com/42wim/matterbridge/bridge/slack"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["slack-legacy"] = bslack.NewLegacy
 | 
				
			||||||
 | 
						FullMap["slack"] = bslack.New
 | 
				
			||||||
 | 
						UserTypingSupport["slack"] = struct{}{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !nosshchat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["sshchat"] = bsshchat.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !nosteam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						bsteam "github.com/42wim/matterbridge/bridge/steam"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["steam"] = bsteam.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !notelegram
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						btelegram "github.com/42wim/matterbridge/bridge/telegram"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["telegram"] = btelegram.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !nowhatsapp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["whatsapp"] = bwhatsapp.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !noxmpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["xmpp"] = bxmpp.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// +build !nozulip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package bridgemap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						bzulip "github.com/42wim/matterbridge/bridge/zulip"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						FullMap["zulip"] = bzulip.New
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -108,7 +108,7 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
 | 
				
			|||||||
func (gw *Gateway) checkConfig(cfg *config.Bridge) {
 | 
					func (gw *Gateway) checkConfig(cfg *config.Bridge) {
 | 
				
			||||||
	match := false
 | 
						match := false
 | 
				
			||||||
	for _, key := range gw.Router.Config.Viper().AllKeys() {
 | 
						for _, key := range gw.Router.Config.Viper().AllKeys() {
 | 
				
			||||||
		if strings.HasPrefix(key, cfg.Account) {
 | 
							if strings.HasPrefix(key, strings.ToLower(cfg.Account)) {
 | 
				
			||||||
			match = true
 | 
								match = true
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -306,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, "")
 | 
				
			||||||
@@ -315,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]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -169,7 +169,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
 | 
				
			|||||||
	switch event {
 | 
						switch event {
 | 
				
			||||||
	case config.EventAvatarDownload:
 | 
						case config.EventAvatarDownload:
 | 
				
			||||||
		// Avatar downloads are only relevant for telegram and mattermost for now
 | 
							// Avatar downloads are only relevant for telegram and mattermost for now
 | 
				
			||||||
		if dest.Protocol != "mattermost" && dest.Protocol != "telegram" {
 | 
							if dest.Protocol != "mattermost" && dest.Protocol != "telegram" && dest.Protocol != "xmpp" {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case config.EventJoinLeave:
 | 
						case config.EventJoinLeave:
 | 
				
			||||||
@@ -179,7 +179,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	case config.EventTopicChange:
 | 
						case config.EventTopicChange:
 | 
				
			||||||
		// only relay topic change when used in some way on other side
 | 
							// only relay topic change when used in some way on other side
 | 
				
			||||||
		if dest.GetBool("ShowTopicChange") && dest.GetBool("SyncTopic") {
 | 
							if !dest.GetBool("ShowTopicChange") && !dest.GetBool("SyncTopic") {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,6 +132,9 @@ func (r *Router) handleReceive() {
 | 
				
			|||||||
		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
 | 
							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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,58 +5,51 @@ require (
 | 
				
			|||||||
	github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
 | 
						github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
 | 
				
			||||||
	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.20190816133340-b04c5a83c1c0
 | 
						github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
 | 
				
			||||||
	github.com/Rhymen/go-whatsapp v0.1.0
 | 
						github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334
 | 
				
			||||||
	github.com/bwmarrin/discordgo v0.20.2
 | 
						github.com/d5/tengo/v2 v2.6.0
 | 
				
			||||||
	github.com/d5/tengo/v2 v2.0.2
 | 
						github.com/davecgh/go-spew v1.1.1
 | 
				
			||||||
	github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
 | 
						github.com/fsnotify/fsnotify v1.4.9
 | 
				
			||||||
	github.com/fsnotify/fsnotify v1.4.7
 | 
						github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81
 | 
				
			||||||
	github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
 | 
						github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725
 | 
				
			||||||
	github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497
 | 
						github.com/google/gops v0.3.10
 | 
				
			||||||
	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/gorilla/schema v1.1.0
 | 
						github.com/gorilla/schema v1.1.0
 | 
				
			||||||
	github.com/gorilla/websocket v1.4.1
 | 
						github.com/gorilla/websocket v1.4.2
 | 
				
			||||||
	github.com/hashicorp/golang-lru v0.5.3
 | 
						github.com/hashicorp/golang-lru v0.5.4
 | 
				
			||||||
	github.com/hpcloud/tail v1.0.0 // indirect
 | 
					 | 
				
			||||||
	github.com/jpillora/backoff v1.0.0
 | 
						github.com/jpillora/backoff v1.0.0
 | 
				
			||||||
	github.com/keybase/go-keybase-chat-bot v0.0.0-20190816161829-561f10822eb2
 | 
						github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da
 | 
				
			||||||
	github.com/labstack/echo/v4 v4.1.13
 | 
						github.com/labstack/echo/v4 v4.1.16
 | 
				
			||||||
	github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
 | 
						github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
 | 
				
			||||||
	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
 | 
						github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048
 | 
				
			||||||
 | 
						github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d
 | 
				
			||||||
	github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
 | 
						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-20200418225040-c8a3a57b4050
 | 
				
			||||||
	github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca
 | 
						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-20200820220548-be5824faa913
 | 
				
			||||||
	github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
 | 
						github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
 | 
				
			||||||
	github.com/mattermost/mattermost-server v5.5.0+incompatible
 | 
						github.com/mattermost/mattermost-server/v5 v5.25.2
 | 
				
			||||||
 | 
						github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561
 | 
				
			||||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
						github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
				
			||||||
 | 
						github.com/missdeer/golib v1.0.3
 | 
				
			||||||
	github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
 | 
						github.com/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/nlopes/slack v0.6.0
 | 
					 | 
				
			||||||
	github.com/onsi/ginkgo v1.6.0 // indirect
 | 
					 | 
				
			||||||
	github.com/onsi/gomega v1.4.1 // indirect
 | 
					 | 
				
			||||||
	github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
 | 
						github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
 | 
				
			||||||
	github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
 | 
					 | 
				
			||||||
	github.com/rs/xid v1.2.1
 | 
						github.com/rs/xid v1.2.1
 | 
				
			||||||
	github.com/russross/blackfriday v1.5.2
 | 
						github.com/russross/blackfriday v1.5.2
 | 
				
			||||||
	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
 | 
						github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
 | 
				
			||||||
	github.com/shazow/ssh-chat v1.8.2
 | 
						github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98
 | 
				
			||||||
	github.com/sirupsen/logrus v1.4.2
 | 
						github.com/sirupsen/logrus v1.6.0
 | 
				
			||||||
	github.com/spf13/viper v1.6.1
 | 
						github.com/slack-go/slack v0.6.5
 | 
				
			||||||
	github.com/stretchr/testify v1.4.0
 | 
						github.com/spf13/viper v1.7.0
 | 
				
			||||||
	github.com/technoweenie/multipartstreamer v1.0.1 // indirect
 | 
						github.com/stretchr/testify v1.5.1
 | 
				
			||||||
 | 
						github.com/writeas/go-strip-markdown v2.0.1+incompatible
 | 
				
			||||||
	github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
 | 
						github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
 | 
				
			||||||
	github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447
 | 
						github.com/yaegashi/msgraph.go v0.1.3
 | 
				
			||||||
	golang.org/x/image v0.0.0-20191214001246-9130b4cfad52
 | 
						github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
 | 
				
			||||||
	gopkg.in/fsnotify.v1 v1.4.7 // indirect
 | 
						golang.org/x/image v0.0.0-20200618115811-c13761719519
 | 
				
			||||||
	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
 | 
						golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 | 
				
			||||||
	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 | 
						gomod.garykim.dev/nc-talk v0.0.2
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
replace github.com/nlopes/slack v0.6.0 => github.com/matterbridge/slack v0.1.1-0.20191208194820-95190f11bfb6
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
go 1.13
 | 
					go 1.13
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
						"github.com/42wim/matterbridge/bridge/config"
 | 
				
			||||||
@@ -15,7 +16,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	version = "1.16.4-dev"
 | 
						version = "1.18.1"
 | 
				
			||||||
	githash string
 | 
						githash string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flagConfig  = flag.String("conf", "matterbridge.toml", "config file")
 | 
						flagConfig  = flag.String("conf", "matterbridge.toml", "config file")
 | 
				
			||||||
@@ -50,6 +51,15 @@ func main() {
 | 
				
			|||||||
	cfg := config.NewConfig(rootLogger, *flagConfig)
 | 
						cfg := config.NewConfig(rootLogger, *flagConfig)
 | 
				
			||||||
	cfg.BridgeValues().General.Debug = *flagDebug
 | 
						cfg.BridgeValues().General.Debug = *flagDebug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if logging to a file, ensure it is closed when the program terminates
 | 
				
			||||||
 | 
						// nolint:errcheck
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if f, ok := rootLogger.Out.(*os.File); ok {
 | 
				
			||||||
 | 
								f.Sync()
 | 
				
			||||||
 | 
								f.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r, err := gateway.NewRouter(rootLogger, cfg, bridgemap.FullMap)
 | 
						r, err := gateway.NewRouter(rootLogger, cfg, bridgemap.FullMap)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logger.Fatalf("Starting gateway failed: %s", err)
 | 
							logger.Fatalf("Starting gateway failed: %s", err)
 | 
				
			||||||
@@ -67,17 +77,31 @@ func setupLogger() *logrus.Logger {
 | 
				
			|||||||
		Formatter: &prefixed.TextFormatter{
 | 
							Formatter: &prefixed.TextFormatter{
 | 
				
			||||||
			PrefixPadding: 13,
 | 
								PrefixPadding: 13,
 | 
				
			||||||
			DisableColors: true,
 | 
								DisableColors: true,
 | 
				
			||||||
			FullTimestamp: true,
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Level: logrus.InfoLevel,
 | 
							Level: logrus.InfoLevel,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if *flagDebug || os.Getenv("DEBUG") == "1" {
 | 
						if *flagDebug || os.Getenv("DEBUG") == "1" {
 | 
				
			||||||
 | 
							logger.SetReportCaller(true)
 | 
				
			||||||
		logger.Formatter = &prefixed.TextFormatter{
 | 
							logger.Formatter = &prefixed.TextFormatter{
 | 
				
			||||||
			PrefixPadding:   13,
 | 
								PrefixPadding: 13,
 | 
				
			||||||
			DisableColors:   true,
 | 
								DisableColors: true,
 | 
				
			||||||
			FullTimestamp:   false,
 | 
								FullTimestamp: false,
 | 
				
			||||||
			ForceFormatting: true,
 | 
					
 | 
				
			||||||
 | 
								CallerFormatter: func(function, file string) string {
 | 
				
			||||||
 | 
									return fmt.Sprintf(" [%s:%s]", function, file)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								CallerPrettyfier: func(f *runtime.Frame) (string, string) {
 | 
				
			||||||
 | 
									sp := strings.SplitAfter(f.File, "/matterbridge/")
 | 
				
			||||||
 | 
									filename := f.File
 | 
				
			||||||
 | 
									if len(sp) > 1 {
 | 
				
			||||||
 | 
										filename = sp[1]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									s := strings.Split(f.Function, ".")
 | 
				
			||||||
 | 
									funcName := s[len(s)-1]
 | 
				
			||||||
 | 
									return funcName, fmt.Sprintf("%s:%d", filename, f.Line)
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		logger.Level = logrus.DebugLevel
 | 
							logger.Level = logrus.DebugLevel
 | 
				
			||||||
		logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.")
 | 
							logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,6 +103,10 @@ ColorNicks=false
 | 
				
			|||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"]
 | 
					RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#StripMarkdown strips markdown from messages
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					StripMarkdown=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Nicks you want to ignore.
 | 
					#Nicks you want to ignore.
 | 
				
			||||||
#Regular expressions supported
 | 
					#Regular expressions supported
 | 
				
			||||||
#Messages from those users will not be sent to other bridges.
 | 
					#Messages from those users will not be sent to other bridges.
 | 
				
			||||||
@@ -177,6 +181,12 @@ StripNick=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowTopicChange=false
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Delay in milliseconds between channel joins
 | 
				
			||||||
 | 
					#Only useful when you have a LOT of channels to join
 | 
				
			||||||
 | 
					#See https://github.com/42wim/matterbridge/issues/1084
 | 
				
			||||||
 | 
					#OPTIONAL (default 0)
 | 
				
			||||||
 | 
					JoinDelay=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#XMPP section
 | 
					#XMPP section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -545,6 +555,96 @@ Label=""
 | 
				
			|||||||
# REQUIRED
 | 
					# REQUIRED
 | 
				
			||||||
Team="myteam"
 | 
					Team="myteam"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					###################################################################
 | 
				
			||||||
 | 
					# Microsoft teams section
 | 
				
			||||||
 | 
					# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup
 | 
				
			||||||
 | 
					###################################################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[msteams.myteam]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TenantID
 | 
				
			||||||
 | 
					# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup#get-necessary-ids-for-matterbridge
 | 
				
			||||||
 | 
					TenantID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ClientID
 | 
				
			||||||
 | 
					# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup#get-necessary-ids-for-matterbridge
 | 
				
			||||||
 | 
					ClientID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TeamID
 | 
				
			||||||
 | 
					# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup#get-necessary-ids-for-matterbridge
 | 
				
			||||||
 | 
					TeamID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## RELOADABLE SETTINGS
 | 
				
			||||||
 | 
					## Settings below can be reloaded by editing the file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Nicks you want to ignore.
 | 
				
			||||||
 | 
					#Regular expressions supported
 | 
				
			||||||
 | 
					#Messages from those users will not be sent to other bridges.
 | 
				
			||||||
 | 
					#OPTIONAL
 | 
				
			||||||
 | 
					IgnoreNicks="ircspammer1 ircspammer2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Messages you want to ignore.
 | 
				
			||||||
 | 
					#Messages matching these regexp will be ignored and not sent to other bridges
 | 
				
			||||||
 | 
					#See https://regex-golang.appspot.com/assets/html/index.html for more regex info
 | 
				
			||||||
 | 
					#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
				
			||||||
 | 
					IgnoreMessages="^~~ badword"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#messages you want to replace.
 | 
				
			||||||
 | 
					#it replaces outgoing messages from the bridge.
 | 
				
			||||||
 | 
					#so you need to place it by the sending bridge definition.
 | 
				
			||||||
 | 
					#regular expressions supported
 | 
				
			||||||
 | 
					#some examples:
 | 
				
			||||||
 | 
					#this replaces cat => dog and sleep => awake
 | 
				
			||||||
 | 
					#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
				
			||||||
 | 
					#this replaces every number with number.  123 => numbernumbernumber
 | 
				
			||||||
 | 
					#replacemessages=[ ["[0-9]","number"] ]
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					ReplaceMessages=[ ["cat","dog"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#nicks you want to replace.
 | 
				
			||||||
 | 
					#see replacemessages for syntaxa
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					ReplaceNicks=[ ["user--","user"] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Extractnicks is used to for example rewrite messages from other relaybots
 | 
				
			||||||
 | 
					#See https://github.com/42wim/matterbridge/issues/713 and https://github.com/42wim/matterbridge/issues/466
 | 
				
			||||||
 | 
					#some examples:
 | 
				
			||||||
 | 
					#this replaces a message like "Relaybot: <relayeduser> something interesting" to "relayeduser: something interesting"
 | 
				
			||||||
 | 
					#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ]
 | 
				
			||||||
 | 
					#you can use multiple entries for multiplebots
 | 
				
			||||||
 | 
					#this also replaces a message like "otherbot: (relayeduser) something else" to "relayeduser: something else"
 | 
				
			||||||
 | 
					#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ]
 | 
				
			||||||
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
 | 
					ExtractNicks=[ ["otherbot","<(.*?)>\\s+" ] ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#extra label that can be used in the RemoteNickFormat
 | 
				
			||||||
 | 
					#optional (default empty)
 | 
				
			||||||
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#RemoteNickFormat defines how remote users appear on this bridge
 | 
				
			||||||
 | 
					#See [general] config section for default options
 | 
				
			||||||
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show users joins/parts from other bridges
 | 
				
			||||||
 | 
					#Currently works for messages from the following bridges: irc, mattermost, slack, discord
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
				
			||||||
 | 
					#It will strip other characters from the nick
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Enable to show topic changes from other bridges
 | 
				
			||||||
 | 
					#Only works hiding/show topic changes from slack bridge for now
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Opportunistically preserve threaded replies between bridges
 | 
				
			||||||
 | 
					#that support threading
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					PreserveThreading=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#slack section
 | 
					#slack section
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -703,114 +803,125 @@ ShowUserTyping=false
 | 
				
			|||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
[discord]
 | 
					[discord]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#You can configure multiple servers "[discord.name]" or "[discord.name2]"
 | 
					# You can configure multiple servers "[discord.name]" or "[discord.name2]"
 | 
				
			||||||
#In this example we use [discord.game]
 | 
					# In this example we use [discord.game]
 | 
				
			||||||
#REQUIRED
 | 
					#REQUIRED
 | 
				
			||||||
[discord.game]
 | 
					[discord.game]
 | 
				
			||||||
#Token to connect with Discord API
 | 
					# Token (REQUIRED) is the token to connect with Discord API
 | 
				
			||||||
#You can get your token by following the instructions on 
 | 
					# You can get your token by following the instructions on
 | 
				
			||||||
#https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token
 | 
					# https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token
 | 
				
			||||||
#If you want roles/groups mentions to be shown with names instead of ID, you'll need to give your bot the "Manage Roles" permission.
 | 
					# If you want roles/groups mentions to be shown with names instead of ID, you'll need to give your bot the "Manage Roles" permission.
 | 
				
			||||||
#REQUIRED
 | 
					 | 
				
			||||||
Token="Yourtokenhere"
 | 
					Token="Yourtokenhere"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#REQUIRED
 | 
					# Server (REQUIRED) is the ID or name of the guild to connect to, selected from the guilds the bot has been invited to
 | 
				
			||||||
Server="yourservername"
 | 
					Server="yourservername"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## RELOADABLE SETTINGS
 | 
					## RELOADABLE SETTINGS
 | 
				
			||||||
## Settings below can be reloaded by editing the file
 | 
					## All settings below can be reloaded by editing the file.
 | 
				
			||||||
 | 
					## They are also all optional.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Shows title, description and URL of embedded messages (sent by other bots)
 | 
					# ShowEmbeds shows the title, description and URL of embedded messages (sent by other bots)
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
ShowEmbeds=false
 | 
					ShowEmbeds=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Shows the username instead of the server nickname
 | 
					# UseLocalAvatar specifies source bridges for which an avatar should be 'guessed' when an incoming message has no avatar.
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					# This works by comparing the username of the message to an existing Discord user, and using the avatar of the Discord user.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This only works if WebhookURL is set (AND the message has no avatar).
 | 
				
			||||||
 | 
					# Example: ["irc"]
 | 
				
			||||||
 | 
					UseLocalAvatar=[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# UseUserName shows the username instead of the server nickname
 | 
				
			||||||
UseUserName=false
 | 
					UseUserName=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Show #xxxx discriminator with UseUserName
 | 
					# UseDiscriminator appends the `#xxxx` discriminator when used with UseUserName
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
UseDiscriminator=false
 | 
					UseDiscriminator=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Specify WebhookURL. If given, will relay messages using the Webhook, which gives a better look to messages.
 | 
					# WebhookURL sends messages in the style of puppets.
 | 
				
			||||||
#This only works if you have one discord channel, if you have multiple discord channels you'll have to specify it in the gateway config
 | 
					# This only works if you have one discord channel, if you have multiple discord channels you'll have to specify it in the gateway config
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					# Example: "https://discordapp.com/api/webhooks/1234/abcd_xyzw"
 | 
				
			||||||
WebhookURL="Yourwebhooktokenhere"
 | 
					WebhookURL=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Disable sending of edits to other bridges
 | 
					# EditDisable disables sending of edits to other bridges
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
EditDisable=false
 | 
					EditDisable=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Message to be appended to every edited message
 | 
					# EditSuffix specifies the message to be appended to every edited message
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					# Example: " (edited)"
 | 
				
			||||||
EditSuffix=" (edited)"
 | 
					EditSuffix=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Nicks you want to ignore. 
 | 
					# IgnoreNicks mutes outgoing messages from certain users.
 | 
				
			||||||
#Regular expressions supported
 | 
					# Messages from these users will not be transmitted to other bridges.
 | 
				
			||||||
#Messages from those users will not be sent to other bridges.
 | 
					# Regular expressions are also supported.
 | 
				
			||||||
#OPTIONAL
 | 
					# Example: "ircspammer1 ircspammer2"
 | 
				
			||||||
IgnoreNicks="ircspammer1 ircspammer2"
 | 
					IgnoreNicks=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Messages you want to ignore. 
 | 
					# IgnoreMessages mutes outgoing messages of a certain format.
 | 
				
			||||||
#Messages matching these regexp will be ignored and not sent to other bridges
 | 
					# Messages matching this regular expression will not be transmitted sent to other bridges
 | 
				
			||||||
#See https://regex-golang.appspot.com/assets/html/index.html for more regex info
 | 
					# See https://regex-golang.appspot.com/assets/html/index.html for more regex info
 | 
				
			||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
 | 
					#
 | 
				
			||||||
IgnoreMessages="^~~ badword"
 | 
					# Example that ignores messages starting with ~~ or messages containing badword:
 | 
				
			||||||
 | 
					#   IgnoreMessages="^~~ badword"
 | 
				
			||||||
 | 
					IgnoreMessages=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#messages you want to replace.
 | 
					# ReplaceMessages replaces substrings of messages in outgoing messages.
 | 
				
			||||||
#it replaces outgoing messages from the bridge.
 | 
					# Regular expressions are supported.
 | 
				
			||||||
#so you need to place it by the sending bridge definition.
 | 
					#
 | 
				
			||||||
#regular expressions supported
 | 
					# Example that replaces 'cat' => 'dog' and 'sleep' => 'awake':
 | 
				
			||||||
#some examples:
 | 
					#   ReplaceMessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
				
			||||||
#this replaces cat => dog and sleep => awake
 | 
					# Example that replaces all digits with the letter 'X', so 'hello123' becomes 'helloXXX':
 | 
				
			||||||
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
 | 
					#   ReplaceMessages=[ ["[0-9]","X"] ]
 | 
				
			||||||
#this replaces every number with number.  123 => numbernumbernumber
 | 
					ReplaceMessages=[]
 | 
				
			||||||
#replacemessages=[ ["[0-9]","number"] ]
 | 
					 | 
				
			||||||
#optional (default empty)
 | 
					 | 
				
			||||||
ReplaceMessages=[ ["cat","dog"] ]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#nicks you want to replace.
 | 
					# ReplaceNicks replaces substrings of usernames in outgoing messages.
 | 
				
			||||||
#see replacemessages for syntaxa
 | 
					# See the ReplaceMessages setting for examples.
 | 
				
			||||||
#optional (default empty)
 | 
					# Example: [ ["user--","user"] ]
 | 
				
			||||||
ReplaceNicks=[ ["user--","user"] ]
 | 
					ReplaceNicks=[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Extractnicks is used to for example rewrite messages from other relaybots
 | 
					# ExtractNicks allows for interoperability with other bridge software by rewriting messages and extracting usernames.
 | 
				
			||||||
#See https://github.com/42wim/matterbridge/issues/713 and https://github.com/42wim/matterbridge/issues/466
 | 
					#
 | 
				
			||||||
#some examples:
 | 
					# Recommended reading:
 | 
				
			||||||
#this replaces a message like "Relaybot: <relayeduser> something interesting" to "relayeduser: something interesting"
 | 
					# - https://github.com/42wim/matterbridge/issues/466
 | 
				
			||||||
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ]
 | 
					# - https://github.com/42wim/matterbridge/issues/713
 | 
				
			||||||
#you can use multiple entries for multiplebots
 | 
					#
 | 
				
			||||||
#this also replaces a message like "otherbot: (relayeduser) something else" to "relayeduser: something else"
 | 
					# This example translates the following message
 | 
				
			||||||
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ]
 | 
					#   "Relaybot: <relayeduser> something interesting"
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					# into this message
 | 
				
			||||||
ExtractNicks=[ ["otherbot","<(.*?)>\\s+" ] ]
 | 
					#   "relayeduser: something interesting"
 | 
				
			||||||
 | 
					# like so:
 | 
				
			||||||
 | 
					#   ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ]
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This example translates the following message
 | 
				
			||||||
 | 
					#   "otherbot: (relayeduser) something else"
 | 
				
			||||||
 | 
					# into this message
 | 
				
			||||||
 | 
					#   "relayeduser: something else"
 | 
				
			||||||
 | 
					# like so:
 | 
				
			||||||
 | 
					#   ExtractNicks=[ [ "otherbot","\\((.*?)\\)\\s+" ] ]
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This example combines both of the above examples into one:
 | 
				
			||||||
 | 
					#   ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ]
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					ExtractNicks=[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#extra label that can be used in the RemoteNickFormat
 | 
					# Label is as an extra identifier for use in the RemoteNickFormat setting.
 | 
				
			||||||
#optional (default empty)
 | 
					 | 
				
			||||||
Label=""
 | 
					Label=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#RemoteNickFormat defines how remote users appear on this bridge 
 | 
					# RemoteNickFormat formats how remote users appear on this bridge.
 | 
				
			||||||
#See [general] config section for default options
 | 
					# See the [general] config section for default options
 | 
				
			||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
					RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show users joins/parts from other bridges 
 | 
					# ShowJoinPart emits messages that show joins/parts from other bridges
 | 
				
			||||||
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
 | 
					# Supported from the following bridges: irc, mattermost, slack, discord
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
ShowJoinPart=false
 | 
					ShowJoinPart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
 | 
					# StripNick strips non-alphanumeric characters from nicknames.
 | 
				
			||||||
#It will strip other characters from the nick
 | 
					# Recommended reading: https://github.com/42wim/matterbridge/issues/285
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
StripNick=false
 | 
					StripNick=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to show topic/purpose changes from other bridges
 | 
					# ShowTopicChange emits messages that show topic/purpose updates from other bridges
 | 
				
			||||||
#Only works hiding/show topic changes from slack bridge for now
 | 
					# Supported from the following bridges: slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
ShowTopicChange=false
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#Enable to sync topic/purpose changes from other bridges
 | 
					# SyncTopic synchronises topic/purpose updates from other bridges
 | 
				
			||||||
#Only works syncing topic changes from slack bridge for now
 | 
					# Supported from the following bridges: slack
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					 | 
				
			||||||
SyncTopic=false
 | 
					SyncTopic=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -831,10 +942,11 @@ Token="Yourtokenhere"
 | 
				
			|||||||
## Settings below can be reloaded by editing the file
 | 
					## Settings below can be reloaded by editing the file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#OPTIONAL (default empty)
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
#Supported formats are "HTML", "Markdown" and "HTMLNick"
 | 
					#Supported formats are:
 | 
				
			||||||
#See https://core.telegram.org/bots/api#html-style
 | 
					#"HTML" https://core.telegram.org/bots/api#html-style
 | 
				
			||||||
#See https://core.telegram.org/bots/api#markdown-style
 | 
					#"Markdown" https://core.telegram.org/bots/api#markdown-style - deprecated, doesn't display links with underscores correctly
 | 
				
			||||||
#HTMLNick only allows HTML for the nick, the message itself will be html-escaped
 | 
					#"MarkdownV2" https://core.telegram.org/bots/api#markdownv2-style
 | 
				
			||||||
 | 
					#"HTMLNick" - only allows HTML for the nick, the message itself will be html-escaped
 | 
				
			||||||
MessageFormat=""
 | 
					MessageFormat=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
@@ -1101,6 +1213,11 @@ Password="yourpass"
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
NoHomeServerSuffix=false
 | 
					NoHomeServerSuffix=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Whether to disable sending of HTML content to matrix
 | 
				
			||||||
 | 
					#See https://github.com/42wim/matterbridge/issues/1022
 | 
				
			||||||
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
 | 
					HTMLDisable=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## RELOADABLE SETTINGS
 | 
					## RELOADABLE SETTINGS
 | 
				
			||||||
## Settings below can be reloaded by editing the file
 | 
					## Settings below can be reloaded by editing the file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1266,7 +1383,22 @@ StripNick=false
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
ShowTopicChange=false
 | 
					ShowTopicChange=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					###################################################################
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# NCTalk (Nextcloud Talk)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					###################################################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[nctalk.bridge]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Url of your Nextcloud server
 | 
				
			||||||
 | 
					Server = "https://cloud.youdomain.me"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Username of the bot
 | 
				
			||||||
 | 
					Login = "talkuser"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Password of the bot
 | 
				
			||||||
 | 
					Password = "talkuserpass"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
@@ -1487,6 +1619,14 @@ MediaDownloadBlacklist=[".html$",".htm$"]
 | 
				
			|||||||
#OPTIONAL (default false)
 | 
					#OPTIONAL (default false)
 | 
				
			||||||
IgnoreFailureOnStart=false
 | 
					IgnoreFailureOnStart=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#LogFile defines the location of a file to write logs into, rather
 | 
				
			||||||
 | 
					#than stdout.
 | 
				
			||||||
 | 
					#Logging will still happen on stdout if the file cannot be open for
 | 
				
			||||||
 | 
					#writing, or if the value is empty. Note that the log won't roll, so
 | 
				
			||||||
 | 
					#you might want to use logrotate(8) with this feature.
 | 
				
			||||||
 | 
					#OPTIONAL (default empty)
 | 
				
			||||||
 | 
					LogFile="/var/log/matterbridge.log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
#Tengo configuration
 | 
					#Tengo configuration
 | 
				
			||||||
###################################################################
 | 
					###################################################################
 | 
				
			||||||
@@ -1577,32 +1717,46 @@ enable=true
 | 
				
			|||||||
    # REQUIRED
 | 
					    # REQUIRED
 | 
				
			||||||
    account="irc.freenode"
 | 
					    account="irc.freenode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # channel to connect on that account
 | 
					    # The channel key in each gateway is mapped to a similar group chat ID on the chat platform
 | 
				
			||||||
    # How to specify them for the different bridges:
 | 
					    # To find the group chat ID for different platforms, refer to the table below
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # irc        - #channel (# is required) (this needs to be lowercase!)
 | 
					    # Platform   |   Identifier name  |            Example            | Description
 | 
				
			||||||
    # mattermost - channel (the channel name as seen in the URL, not the displayname)
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # gitter     - username/room
 | 
					    #            |      channel       |            general            | Do not include the # symbol
 | 
				
			||||||
    # xmpp       - channel
 | 
					    #  discord   |    channel id      |          ID:123456789         | See https://github.com/42wim/matterbridge/issues/57
 | 
				
			||||||
    # slack      - channel (without the #)
 | 
					    #            | category/channel   |          Media/gaming         | Without # symbol. If you're using discord categories to group your channels
 | 
				
			||||||
    #            - ID:C123456 (where C123456 is the channel ID) does not work with webhook
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # discord    - channel (without the #)
 | 
					    #   gitter   |  username/room     |            general            | As seen in the gitter.im URL
 | 
				
			||||||
    #            - ID:123456789 (where 123456789 is the channel ID)
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    #               (https://github.com/42wim/matterbridge/issues/57)
 | 
					    #   hipchat  |    id_channel      |         example needed        | See https://www.hipchat.com/account/xmpp for the correct channel
 | 
				
			||||||
    #            - category/channel (without the #) if you're using discord categories to group your channels
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # telegram   - chatid (a large negative number, eg -123456789)
 | 
					    #    irc     |      channel       |            #general           | The # symbol is required and should be lowercase!
 | 
				
			||||||
    #             see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau)
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # hipchat    - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel)
 | 
					    # mattermost |      channel       |            general            | This is the channel name as seen in the URL, not the display name
 | 
				
			||||||
    # rocketchat - #channel (# is required (also needed for private channels!)
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # matrix     - #channel:server (eg #yourchannel:matrix.org)
 | 
					    #   matrix   | #channel:server    |    #yourchannel:matrix.org    | Encrypted rooms are not supported in matrix
 | 
				
			||||||
    #            - encrypted rooms are not supported in matrix
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # steam      - chatid (a large number).
 | 
					    #   msteams  |      threadId      |    19:82abcxx@thread.skype    | You'll find the threadId in the URL
 | 
				
			||||||
    #             The number in the URL when you click "enter chat room" in the browser
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    # whatsapp   - 48111222333-123455678999@g.us A unique group JID;
 | 
					    # rocketchat |      channel       |            #channel           | # is required for private channels too
 | 
				
			||||||
    #              if you specify an empty string bridge will list all the possibilities
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
    #            - "Group Name" if you specify a group name the bridge will hint its JID to specify
 | 
					    #   slack    |   channel name     |            general            | Do not include the # symbol
 | 
				
			||||||
    #              as group names might change in time and contain weird emoticons
 | 
					    #            |    channel id      |           ID:C123456          | The underlying ID of a channel. This doesn't work with
 | 
				
			||||||
    # zulip      - stream/topic:topicname (without the #)
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    #   steam    |      chatid        |         example needed        | The number in the URL when you click "enter chat room" in the browser
 | 
				
			||||||
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    #   nctalk   |      token         |           xs25tz5y            | The token in the URL when you are in a chat. It will be the last part of the URL.
 | 
				
			||||||
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    #  telegram  |      chatid        |          -123456789           | A large negative number. see https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
 | 
				
			||||||
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    #  whatsapp  |     group JID      | 48111222333-123455678999@g.us | A unique group JID. If you specify an empty string, bridge will list all the possibilities
 | 
				
			||||||
 | 
					    #            |    "Group Name"    |         "Family Chat"         | if you specify a group name, the bridge will find hint the JID to specify. Names can change over time and are not stable.
 | 
				
			||||||
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    #    xmpp    |      channel       |            general            | The room name
 | 
				
			||||||
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    #   zulip    | stream/topic:topic |     general/off-topic:food    | Do not use the # when specifying a topic
 | 
				
			||||||
 | 
					    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # REQUIRED
 | 
					    # REQUIRED
 | 
				
			||||||
    channel="#testing"
 | 
					    channel="#testing"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetChannels returns all channels we're members off
 | 
					// GetChannels returns all channels we're members off
 | 
				
			||||||
@@ -167,7 +167,7 @@ func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) UpdateChannelsTeam(teamID string) error {
 | 
					func (m *MMClient) UpdateChannelsTeam(teamID string) error {
 | 
				
			||||||
	mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "")
 | 
						mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, false, "")
 | 
				
			||||||
	if resp.Error != nil {
 | 
						if resp.Error != nil {
 | 
				
			||||||
		return errors.New(resp.Error.DetailedError)
 | 
							return errors.New(resp.Error.DetailedError)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/websocket"
 | 
						"github.com/gorilla/websocket"
 | 
				
			||||||
	"github.com/jpillora/backoff"
 | 
						"github.com/jpillora/backoff"
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) doLogin(firstConnection bool, b *backoff.Backoff) error {
 | 
					func (m *MMClient) doLogin(firstConnection bool, b *backoff.Backoff) error {
 | 
				
			||||||
@@ -154,7 +154,7 @@ func (m *MMClient) initUser() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		t := &Team{Team: team, Users: usermap, Id: team.Id}
 | 
							t := &Team{Team: team, Users: usermap, Id: team.Id}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mmchannels, resp := m.Client.GetChannelsForTeamForUser(team.Id, m.User.Id, "")
 | 
							mmchannels, resp := m.Client.GetChannelsForTeamForUser(team.Id, m.User.Id, false, "")
 | 
				
			||||||
		if resp.Error != nil {
 | 
							if resp.Error != nil {
 | 
				
			||||||
			return resp.Error
 | 
								return resp.Error
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ import (
 | 
				
			|||||||
	lru "github.com/hashicorp/golang-lru"
 | 
						lru "github.com/hashicorp/golang-lru"
 | 
				
			||||||
	"github.com/jpillora/backoff"
 | 
						"github.com/jpillora/backoff"
 | 
				
			||||||
	prefixed "github.com/matterbridge/logrus-prefixed-formatter"
 | 
						prefixed "github.com/matterbridge/logrus-prefixed-formatter"
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,6 +69,7 @@ type MMClient struct {
 | 
				
			|||||||
	logger     *logrus.Entry
 | 
						logger     *logrus.Entry
 | 
				
			||||||
	rootLogger *logrus.Logger
 | 
						rootLogger *logrus.Logger
 | 
				
			||||||
	lruCache   *lru.Cache
 | 
						lruCache   *lru.Cache
 | 
				
			||||||
 | 
						allevents  bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New will instantiate a new Matterclient with the specified login details without connecting.
 | 
					// New will instantiate a new Matterclient with the specified login details without connecting.
 | 
				
			||||||
@@ -119,6 +120,10 @@ func (m *MMClient) SetLogLevel(level string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *MMClient) EnableAllEvents() {
 | 
				
			||||||
 | 
						m.allevents = true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Login tries to connect the client with the loging details with which it was initialized.
 | 
					// Login tries to connect the client with the loging details with which it was initialized.
 | 
				
			||||||
func (m *MMClient) Login() error {
 | 
					func (m *MMClient) Login() error {
 | 
				
			||||||
	// check if this is a first connect or a reconnection
 | 
						// check if this is a first connect or a reconnection
 | 
				
			||||||
@@ -220,6 +225,10 @@ func (m *MMClient) WsReceiver() {
 | 
				
			|||||||
					continue
 | 
										continue
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if m.allevents {
 | 
				
			||||||
 | 
									m.MessageChan <- msg
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			switch msg.Raw.Event {
 | 
								switch msg.Raw.Event {
 | 
				
			||||||
			case model.WEBSOCKET_EVENT_USER_ADDED,
 | 
								case model.WEBSOCKET_EVENT_USER_ADDED,
 | 
				
			||||||
				model.WEBSOCKET_EVENT_USER_REMOVED,
 | 
									model.WEBSOCKET_EVENT_USER_REMOVED,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ package matterclient
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) parseActionPost(rmsg *Message) {
 | 
					func (m *MMClient) parseActionPost(rmsg *Message) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mattermost/mattermost-server/model"
 | 
						"github.com/mattermost/mattermost-server/v5/model"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MMClient) GetNickName(userId string) string { //nolint:golint
 | 
					func (m *MMClient) GetNickName(userId string) string { //nolint:golint
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1481
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1481
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										76
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -56,6 +56,8 @@ message Location {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message Point {
 | 
					message Point {
 | 
				
			||||||
 | 
					    optional int32 xDeprecated = 1;
 | 
				
			||||||
 | 
					    optional int32 yDeprecated = 2;
 | 
				
			||||||
    optional double x = 3;
 | 
					    optional double x = 3;
 | 
				
			||||||
    optional double y = 4;
 | 
					    optional double y = 4;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -93,6 +95,7 @@ message ContextInfo {
 | 
				
			|||||||
    optional AdReplyInfo quotedAd = 23;
 | 
					    optional AdReplyInfo quotedAd = 23;
 | 
				
			||||||
    optional MessageKey placeholderKey = 24;
 | 
					    optional MessageKey placeholderKey = 24;
 | 
				
			||||||
    optional uint32 expiration = 25;
 | 
					    optional uint32 expiration = 25;
 | 
				
			||||||
 | 
					    optional int64 ephemeralSettingTimestamp = 26;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message SenderKeyDistributionMessage {
 | 
					message SenderKeyDistributionMessage {
 | 
				
			||||||
@@ -136,6 +139,11 @@ message LocationMessage {
 | 
				
			|||||||
    optional string name = 3;
 | 
					    optional string name = 3;
 | 
				
			||||||
    optional string address = 4;
 | 
					    optional string address = 4;
 | 
				
			||||||
    optional string url = 5;
 | 
					    optional string url = 5;
 | 
				
			||||||
 | 
					    optional bool isLive = 6;
 | 
				
			||||||
 | 
					    optional uint32 accuracyInMeters = 7;
 | 
				
			||||||
 | 
					    optional float speedInMps = 8;
 | 
				
			||||||
 | 
					    optional uint32 degreesClockwiseFromMagneticNorth = 9;
 | 
				
			||||||
 | 
					    optional string comment = 11;
 | 
				
			||||||
    optional bytes jpegThumbnail = 16;
 | 
					    optional bytes jpegThumbnail = 16;
 | 
				
			||||||
    optional ContextInfo contextInfo = 17;
 | 
					    optional ContextInfo contextInfo = 17;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -238,9 +246,29 @@ message ProtocolMessage {
 | 
				
			|||||||
    enum PROTOCOL_MESSAGE_TYPE {
 | 
					    enum PROTOCOL_MESSAGE_TYPE {
 | 
				
			||||||
        REVOKE = 0;
 | 
					        REVOKE = 0;
 | 
				
			||||||
        EPHEMERAL_SETTING = 3;
 | 
					        EPHEMERAL_SETTING = 3;
 | 
				
			||||||
 | 
					        EPHEMERAL_SYNC_RESPONSE = 4;
 | 
				
			||||||
 | 
					        HISTORY_SYNC_NOTIFICATION = 5;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    optional PROTOCOL_MESSAGE_TYPE type = 2;
 | 
					    optional PROTOCOL_MESSAGE_TYPE type = 2;
 | 
				
			||||||
    optional uint32 ephemeralExpiration = 4;
 | 
					    optional uint32 ephemeralExpiration = 4;
 | 
				
			||||||
 | 
					    optional int64 ephemeralSettingTimestamp = 5;
 | 
				
			||||||
 | 
					    optional HistorySyncNotification historySyncNotification = 6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message HistorySyncNotification {
 | 
				
			||||||
 | 
					    optional bytes fileSha256 = 1;
 | 
				
			||||||
 | 
					    optional uint64 fileLength = 2;
 | 
				
			||||||
 | 
					    optional bytes mediaKey = 3;
 | 
				
			||||||
 | 
					    optional bytes fileEncSha256 = 4;
 | 
				
			||||||
 | 
					    optional string directPath = 5;
 | 
				
			||||||
 | 
					    enum HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE {
 | 
				
			||||||
 | 
					        INITIAL_BOOTSTRAP = 0;
 | 
				
			||||||
 | 
					        INITIAL_STATUS_V3 = 1;
 | 
				
			||||||
 | 
					        FULL = 2;
 | 
				
			||||||
 | 
					        RECENT = 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    optional HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE syncType = 6;
 | 
				
			||||||
 | 
					    optional uint32 chunkOrder = 7;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message ContactsArrayMessage {
 | 
					message ContactsArrayMessage {
 | 
				
			||||||
@@ -355,6 +383,8 @@ message StickerMessage {
 | 
				
			|||||||
    optional int64 mediaKeyTimestamp = 10;
 | 
					    optional int64 mediaKeyTimestamp = 10;
 | 
				
			||||||
    optional uint32 firstFrameLength = 11;
 | 
					    optional uint32 firstFrameLength = 11;
 | 
				
			||||||
    optional bytes firstFrameSidecar = 12;
 | 
					    optional bytes firstFrameSidecar = 12;
 | 
				
			||||||
 | 
					    optional bool isAnimated = 13;
 | 
				
			||||||
 | 
					    optional bytes pngThumbnail = 16;
 | 
				
			||||||
    optional ContextInfo contextInfo = 17;
 | 
					    optional ContextInfo contextInfo = 17;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -401,6 +431,12 @@ message TemplateButtonReplyMessage {
 | 
				
			|||||||
    optional uint32 selectedIndex = 4;
 | 
					    optional uint32 selectedIndex = 4;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message CatalogSnapshot {
 | 
				
			||||||
 | 
					    optional ImageMessage catalogImage = 1;
 | 
				
			||||||
 | 
					    optional string title = 2;
 | 
				
			||||||
 | 
					    optional string description = 3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message ProductSnapshot {
 | 
					message ProductSnapshot {
 | 
				
			||||||
    optional ImageMessage productImage = 1;
 | 
					    optional ImageMessage productImage = 1;
 | 
				
			||||||
    optional string productId = 2;
 | 
					    optional string productId = 2;
 | 
				
			||||||
@@ -417,6 +453,7 @@ message ProductSnapshot {
 | 
				
			|||||||
message ProductMessage {
 | 
					message ProductMessage {
 | 
				
			||||||
    optional ProductSnapshot product = 1;
 | 
					    optional ProductSnapshot product = 1;
 | 
				
			||||||
    optional string businessOwnerJid = 2;
 | 
					    optional string businessOwnerJid = 2;
 | 
				
			||||||
 | 
					    optional CatalogSnapshot catalog = 4;
 | 
				
			||||||
    optional ContextInfo contextInfo = 17;
 | 
					    optional ContextInfo contextInfo = 17;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -513,6 +550,8 @@ message WebFeatures {
 | 
				
			|||||||
    optional WEB_FEATURES_FLAG templateMessage = 30;
 | 
					    optional WEB_FEATURES_FLAG templateMessage = 30;
 | 
				
			||||||
    optional WEB_FEATURES_FLAG templateMessageInteractivity = 31;
 | 
					    optional WEB_FEATURES_FLAG templateMessageInteractivity = 31;
 | 
				
			||||||
    optional WEB_FEATURES_FLAG ephemeralMessages = 32;
 | 
					    optional WEB_FEATURES_FLAG ephemeralMessages = 32;
 | 
				
			||||||
 | 
					    optional WEB_FEATURES_FLAG e2ENotificationSync = 33;
 | 
				
			||||||
 | 
					    optional WEB_FEATURES_FLAG recentStickersV2 = 34;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message TabletNotificationsInfo {
 | 
					message TabletNotificationsInfo {
 | 
				
			||||||
@@ -537,6 +576,11 @@ message WebNotificationsInfo {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message PaymentInfo {
 | 
					message PaymentInfo {
 | 
				
			||||||
 | 
					    enum PAYMENT_INFO_CURRENCY {
 | 
				
			||||||
 | 
					        UNKNOWN_CURRENCY = 0;
 | 
				
			||||||
 | 
					        INR = 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    optional PAYMENT_INFO_CURRENCY currencyDeprecated = 1;
 | 
				
			||||||
    optional uint64 amount1000 = 2;
 | 
					    optional uint64 amount1000 = 2;
 | 
				
			||||||
    optional string receiverJid = 3;
 | 
					    optional string receiverJid = 3;
 | 
				
			||||||
    enum PAYMENT_INFO_STATUS {
 | 
					    enum PAYMENT_INFO_STATUS {
 | 
				
			||||||
@@ -559,6 +603,37 @@ message PaymentInfo {
 | 
				
			|||||||
    optional uint64 expiryTimestamp = 7;
 | 
					    optional uint64 expiryTimestamp = 7;
 | 
				
			||||||
    optional bool futureproofed = 8;
 | 
					    optional bool futureproofed = 8;
 | 
				
			||||||
    optional string currency = 9;
 | 
					    optional string currency = 9;
 | 
				
			||||||
 | 
					    enum PAYMENT_INFO_TXNSTATUS {
 | 
				
			||||||
 | 
					        UNKNOWN = 0;
 | 
				
			||||||
 | 
					        PENDING_SETUP = 1;
 | 
				
			||||||
 | 
					        PENDING_RECEIVER_SETUP = 2;
 | 
				
			||||||
 | 
					        INIT = 3;
 | 
				
			||||||
 | 
					        SUCCESS = 4;
 | 
				
			||||||
 | 
					        COMPLETED = 5;
 | 
				
			||||||
 | 
					        FAILED = 6;
 | 
				
			||||||
 | 
					        FAILED_RISK = 7;
 | 
				
			||||||
 | 
					        FAILED_PROCESSING = 8;
 | 
				
			||||||
 | 
					        FAILED_RECEIVER_PROCESSING = 9;
 | 
				
			||||||
 | 
					        FAILED_DA = 10;
 | 
				
			||||||
 | 
					        FAILED_DA_FINAL = 11;
 | 
				
			||||||
 | 
					        REFUNDED_TXN = 12;
 | 
				
			||||||
 | 
					        REFUND_FAILED = 13;
 | 
				
			||||||
 | 
					        REFUND_FAILED_PROCESSING = 14;
 | 
				
			||||||
 | 
					        REFUND_FAILED_DA = 15;
 | 
				
			||||||
 | 
					        EXPIRED_TXN = 16;
 | 
				
			||||||
 | 
					        AUTH_CANCELED = 17;
 | 
				
			||||||
 | 
					        AUTH_CANCEL_FAILED_PROCESSING = 18;
 | 
				
			||||||
 | 
					        AUTH_CANCEL_FAILED = 19;
 | 
				
			||||||
 | 
					        COLLECT_INIT = 20;
 | 
				
			||||||
 | 
					        COLLECT_SUCCESS = 21;
 | 
				
			||||||
 | 
					        COLLECT_FAILED = 22;
 | 
				
			||||||
 | 
					        COLLECT_FAILED_RISK = 23;
 | 
				
			||||||
 | 
					        COLLECT_REJECTED = 24;
 | 
				
			||||||
 | 
					        COLLECT_EXPIRED = 25;
 | 
				
			||||||
 | 
					        COLLECT_CANCELED = 26;
 | 
				
			||||||
 | 
					        COLLECT_CANCELLING = 27;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    optional PAYMENT_INFO_TXNSTATUS txnStatus = 10;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message WebMessageInfo {
 | 
					message WebMessageInfo {
 | 
				
			||||||
@@ -669,3 +744,4 @@ message WebMessageInfo {
 | 
				
			|||||||
    optional uint64 ephemeralStartTimestamp = 32;
 | 
					    optional uint64 ephemeralStartTimestamp = 32;
 | 
				
			||||||
    optional uint32 ephemeralDuration = 33;
 | 
					    optional uint32 ephemeralDuration = 33;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -90,6 +90,7 @@ type Conn struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	longClientName  string
 | 
						longClientName  string
 | 
				
			||||||
	shortClientName string
 | 
						shortClientName string
 | 
				
			||||||
 | 
						clientVersion   string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	loginSessionLock sync.RWMutex
 | 
						loginSessionLock sync.RWMutex
 | 
				
			||||||
	Proxy            func(*http.Request) (*url.URL, error)
 | 
						Proxy            func(*http.Request) (*url.URL, error)
 | 
				
			||||||
@@ -121,6 +122,7 @@ func NewConn(timeout time.Duration) (*Conn, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		longClientName:  "github.com/rhymen/go-whatsapp",
 | 
							longClientName:  "github.com/rhymen/go-whatsapp",
 | 
				
			||||||
		shortClientName: "go-whatsapp",
 | 
							shortClientName: "go-whatsapp",
 | 
				
			||||||
 | 
							clientVersion:   "0.1.0",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return wac, wac.connect()
 | 
						return wac, wac.connect()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -135,6 +137,7 @@ func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		longClientName:  "github.com/rhymen/go-whatsapp",
 | 
							longClientName:  "github.com/rhymen/go-whatsapp",
 | 
				
			||||||
		shortClientName: "go-whatsapp",
 | 
							shortClientName: "go-whatsapp",
 | 
				
			||||||
 | 
							clientVersion:   "0.1.0",
 | 
				
			||||||
		Proxy:           proxy,
 | 
							Proxy:           proxy,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return wac, wac.connect()
 | 
						return wac, wac.connect()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@ require (
 | 
				
			|||||||
	github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect
 | 
						github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect
 | 
				
			||||||
	github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect
 | 
						github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect
 | 
				
			||||||
	github.com/golang/protobuf v1.3.0
 | 
						github.com/golang/protobuf v1.3.0
 | 
				
			||||||
	github.com/gorilla/websocket v1.4.0
 | 
						github.com/gorilla/websocket v1.4.1
 | 
				
			||||||
	github.com/pkg/errors v0.8.1
 | 
						github.com/pkg/errors v0.8.1
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
 | 
						golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,8 +12,9 @@ github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc
 | 
				
			|||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
				
			||||||
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
 | 
					github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
 | 
				
			||||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
 | 
					github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
 | 
				
			||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
					 | 
				
			||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
					github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
				
			||||||
 | 
					github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
 | 
				
			||||||
 | 
					github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
 | 
					github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 | 
					github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
 | 
					github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										116
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -10,10 +10,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"mime/multipart"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Rhymen/go-whatsapp/crypto/cbc"
 | 
						"github.com/Rhymen/go-whatsapp/crypto/cbc"
 | 
				
			||||||
@@ -95,7 +93,50 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) {
 | 
				
			|||||||
	return data[:n-10], data[n-10 : n], nil
 | 
						return data[:n-10], data[n-10 : n], nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
 | 
					type MediaConn struct {
 | 
				
			||||||
 | 
						Status    int `json:"status"`
 | 
				
			||||||
 | 
						MediaConn struct {
 | 
				
			||||||
 | 
							Auth  string `json:"auth"`
 | 
				
			||||||
 | 
							TTL   int    `json:"ttl"`
 | 
				
			||||||
 | 
							Hosts []struct {
 | 
				
			||||||
 | 
								Hostname string   `json:"hostname"`
 | 
				
			||||||
 | 
								IPs      []string `json:"ips"`
 | 
				
			||||||
 | 
							} `json:"hosts"`
 | 
				
			||||||
 | 
						} `json:"media_conn"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) {
 | 
				
			||||||
 | 
						queryReq := []interface{}{"query", "mediaConn"}
 | 
				
			||||||
 | 
						ch, err := wac.writeJson(queryReq)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp MediaConn
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case r := <-ch:
 | 
				
			||||||
 | 
							if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
				
			||||||
 | 
								return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case <-time.After(wac.msgTimeout):
 | 
				
			||||||
 | 
							return "", "", 0, fmt.Errorf("query media conn timed out")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resp.Status != 200 {
 | 
				
			||||||
 | 
							return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp.MediaConn.Hosts[0].Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var mediaTypeMap = map[MediaType]string{
 | 
				
			||||||
 | 
						MediaImage: "/mms/image",
 | 
				
			||||||
 | 
						MediaVideo: "/mms/video",
 | 
				
			||||||
 | 
						MediaDocument: "/mms/document",
 | 
				
			||||||
 | 
						MediaAudio: "/mms/audio",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
 | 
				
			||||||
	data, err := ioutil.ReadAll(reader)
 | 
						data, err := ioutil.ReadAll(reader)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", nil, nil, nil, 0, err
 | 
							return "", nil, nil, nil, 0, err
 | 
				
			||||||
@@ -128,67 +169,30 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK
 | 
				
			|||||||
	sha.Write(append(enc, mac...))
 | 
						sha.Write(append(enc, mac...))
 | 
				
			||||||
	fileEncSha256 = sha.Sum(nil)
 | 
						fileEncSha256 = sha.Sum(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var filetype string
 | 
						hostname, auth, _, err := wac.queryMediaConn()
 | 
				
			||||||
	switch appInfo {
 | 
						token := base64.URLEncoding.EncodeToString(fileEncSha256)
 | 
				
			||||||
	case MediaImage:
 | 
						q := url.Values{
 | 
				
			||||||
		filetype = "image"
 | 
							"auth":  []string{auth},
 | 
				
			||||||
	case MediaAudio:
 | 
							"token": []string{token},
 | 
				
			||||||
		filetype = "audio"
 | 
						}
 | 
				
			||||||
	case MediaDocument:
 | 
						path := mediaTypeMap[appInfo]
 | 
				
			||||||
		filetype = "document"
 | 
						uploadURL := url.URL{
 | 
				
			||||||
	case MediaVideo:
 | 
							Scheme:   "https",
 | 
				
			||||||
		filetype = "video"
 | 
							Host:     hostname,
 | 
				
			||||||
 | 
							Path:     fmt.Sprintf("%s/%s", path, token),
 | 
				
			||||||
 | 
							RawQuery: q.Encode(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
 | 
						body := bytes.NewReader(append(enc, mac...))
 | 
				
			||||||
	ch, err := wac.writeJson(uploadReq)
 | 
					
 | 
				
			||||||
 | 
						req, err := http.NewRequest("POST", uploadURL.String(), body)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", nil, nil, nil, 0, err
 | 
							return "", nil, nil, nil, 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var resp map[string]interface{}
 | 
					 | 
				
			||||||
	select {
 | 
					 | 
				
			||||||
	case r := <-ch:
 | 
					 | 
				
			||||||
		if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
					 | 
				
			||||||
			return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case <-time.After(wac.msgTimeout):
 | 
					 | 
				
			||||||
		return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if int(resp["status"].(float64)) != 200 {
 | 
					 | 
				
			||||||
		return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var b bytes.Buffer
 | 
					 | 
				
			||||||
	w := multipart.NewWriter(&b)
 | 
					 | 
				
			||||||
	hashWriter, err := w.CreateFormField("hash")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Fprintf(os.Stderr, "%v\n", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fileWriter, err := w.CreateFormFile("file", "blob")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Fprintf(os.Stderr, "%v\n", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	io.Copy(fileWriter, bytes.NewReader(append(enc, mac...)))
 | 
					 | 
				
			||||||
	err = w.Close()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Fprintf(os.Stderr, "%v\n", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req, err := http.NewRequest("POST", resp["url"].(string), &b)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", nil, nil, nil, 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req.Header.Set("Content-Type", w.FormDataContentType())
 | 
					 | 
				
			||||||
	req.Header.Set("Origin", "https://web.whatsapp.com")
 | 
						req.Header.Set("Origin", "https://web.whatsapp.com")
 | 
				
			||||||
	req.Header.Set("Referer", "https://web.whatsapp.com/")
 | 
						req.Header.Set("Referer", "https://web.whatsapp.com/")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req.URL.Query().Set("f", "j")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	client := &http.Client{}
 | 
						client := &http.Client{}
 | 
				
			||||||
	// Submit the request
 | 
						// Submit the request
 | 
				
			||||||
	res, err := client.Do(req)
 | 
						res, err := client.Do(req)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,7 +15,10 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (wac *Conn) readPump() {
 | 
					func (wac *Conn) readPump() {
 | 
				
			||||||
	defer wac.wg.Done()
 | 
						defer func() {
 | 
				
			||||||
 | 
							wac.wg.Done()
 | 
				
			||||||
 | 
							_, _ = wac.Disconnect()
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var readErr error
 | 
						var readErr error
 | 
				
			||||||
	var msgType int
 | 
						var msgType int
 | 
				
			||||||
@@ -31,7 +34,6 @@ func (wac *Conn) readPump() {
 | 
				
			|||||||
		case <-readerFound:
 | 
							case <-readerFound:
 | 
				
			||||||
			if readErr != nil {
 | 
								if readErr != nil {
 | 
				
			||||||
				wac.handle(&ErrConnectionFailed{Err: readErr})
 | 
									wac.handle(&ErrConnectionFailed{Err: readErr})
 | 
				
			||||||
				_, _ = wac.Disconnect()
 | 
					 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			msg, err := ioutil.ReadAll(reader)
 | 
								msg, err := ioutil.ReadAll(reader)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//represents the WhatsAppWeb client version
 | 
					//represents the WhatsAppWeb client version
 | 
				
			||||||
var waVersion = []int{0, 3, 3324}
 | 
					var waVersion = []int{0, 4, 2080}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
Session contains session individual information. To be able to resume the connection without scanning the qr code
 | 
					Session contains session individual information. To be able to resume the connection without scanning the qr code
 | 
				
			||||||
@@ -107,10 +107,10 @@ func CheckCurrentServerVersion() ([]int, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b64ClientId := base64.StdEncoding.EncodeToString(clientId)
 | 
						b64ClientId := base64.StdEncoding.EncodeToString(clientId)
 | 
				
			||||||
	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true}
 | 
						login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true}
 | 
				
			||||||
	loginChan, err := wac.writeJson(login)
 | 
						loginChan, err := wac.writeJson(login)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("error writing login", err)
 | 
							return nil, fmt.Errorf("error writing login: %s", err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Retrieve an answer from the websocket
 | 
						// Retrieve an answer from the websocket
 | 
				
			||||||
@@ -123,7 +123,7 @@ func CheckCurrentServerVersion() ([]int, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var resp map[string]interface{}
 | 
						var resp map[string]interface{}
 | 
				
			||||||
	if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
						if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("error decoding login", err)
 | 
							return nil, fmt.Errorf("error decoding login: %s", err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Take the curr property as X.Y.Z and split it into as int slice
 | 
						// Take the curr property as X.Y.Z and split it into as int slice
 | 
				
			||||||
@@ -141,17 +141,17 @@ func CheckCurrentServerVersion() ([]int, error) {
 | 
				
			|||||||
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
 | 
					SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
 | 
				
			||||||
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
 | 
					WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
func (wac *Conn) SetClientName(long, short string) error {
 | 
					func (wac *Conn) SetClientName(long, short, version string) error {
 | 
				
			||||||
	if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
 | 
						if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
 | 
				
			||||||
		return fmt.Errorf("cannot change client name after logging in")
 | 
							return fmt.Errorf("cannot change client name after logging in")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wac.longClientName, wac.shortClientName = long, short
 | 
						wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
SetClientVersion sets WhatsApp client version
 | 
					SetClientVersion sets WhatsApp client version
 | 
				
			||||||
Default value is 0.3.3324
 | 
					Default value is 0.4.2080
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
 | 
					func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
 | 
				
			||||||
	waVersion = []int{major, minor, patch}
 | 
						waVersion = []int{major, minor, patch}
 | 
				
			||||||
@@ -213,7 +213,7 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session.ClientId = base64.StdEncoding.EncodeToString(clientId)
 | 
						session.ClientId = base64.StdEncoding.EncodeToString(clientId)
 | 
				
			||||||
	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
 | 
						login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true}
 | 
				
			||||||
	loginChan, err := wac.writeJson(login)
 | 
						loginChan, err := wac.writeJson(login)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return session, fmt.Errorf("error writing login: %v\n", err)
 | 
							return session, fmt.Errorf("error writing login: %v\n", err)
 | 
				
			||||||
@@ -369,7 +369,7 @@ func (wac *Conn) Restore() error {
 | 
				
			|||||||
	wac.listener.Unlock()
 | 
						wac.listener.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//admin init
 | 
						//admin init
 | 
				
			||||||
	init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true}
 | 
						init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true}
 | 
				
			||||||
	initChan, err := wac.writeJson(init)
 | 
						initChan, err := wac.writeJson(init)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("error writing admin init: %v\n", err)
 | 
							return fmt.Errorf("error writing admin init: %v\n", err)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								vendor/github.com/blang/semver/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/blang/semver/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					language: go
 | 
				
			||||||
 | 
					matrix:
 | 
				
			||||||
 | 
					  include:
 | 
				
			||||||
 | 
					  - go: 1.4.3
 | 
				
			||||||
 | 
					  - go: 1.5.4
 | 
				
			||||||
 | 
					  - go: 1.6.3
 | 
				
			||||||
 | 
					  - go: 1.7
 | 
				
			||||||
 | 
					  - go: tip
 | 
				
			||||||
 | 
					  allow_failures:
 | 
				
			||||||
 | 
					  - go: tip
 | 
				
			||||||
 | 
					install:
 | 
				
			||||||
 | 
					- go get golang.org/x/tools/cmd/cover
 | 
				
			||||||
 | 
					- go get github.com/mattn/goveralls
 | 
				
			||||||
 | 
					script:
 | 
				
			||||||
 | 
					- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
 | 
				
			||||||
 | 
					  -repotoken $COVERALLS_TOKEN
 | 
				
			||||||
 | 
					- echo "Build examples" ; cd examples && go build
 | 
				
			||||||
 | 
					- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
 | 
				
			||||||
 | 
					env:
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=
 | 
				
			||||||
							
								
								
									
										22
									
								
								vendor/github.com/blang/semver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/blang/semver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					The MIT License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2014 Benedikt Lang <github at benediktlang.de>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										194
									
								
								vendor/github.com/blang/semver/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								vendor/github.com/blang/semver/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
				
			|||||||
 | 
					semver for golang [](https://travis-ci.org/blang/semver) [](https://godoc.org/github.com/blang/semver) [](https://coveralls.io/r/blang/semver?branch=master)
 | 
				
			||||||
 | 
					======
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ go get github.com/blang/semver
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Note: Always vendor your dependencies or fix on a specific version tag.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					import github.com/blang/semver
 | 
				
			||||||
 | 
					v1, err := semver.Make("1.0.0-beta")
 | 
				
			||||||
 | 
					v2, err := semver.Make("2.0.0-beta")
 | 
				
			||||||
 | 
					v1.Compare(v2)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Why should I use this lib?
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Fully spec compatible
 | 
				
			||||||
 | 
					- No reflection
 | 
				
			||||||
 | 
					- No regex
 | 
				
			||||||
 | 
					- Fully tested (Coverage >99%)
 | 
				
			||||||
 | 
					- Readable parsing/validation errors
 | 
				
			||||||
 | 
					- Fast (See [Benchmarks](#benchmarks))
 | 
				
			||||||
 | 
					- Only Stdlib
 | 
				
			||||||
 | 
					- Uses values instead of pointers
 | 
				
			||||||
 | 
					- Many features, see below
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Features
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Parsing and validation at all levels
 | 
				
			||||||
 | 
					- Comparator-like comparisons
 | 
				
			||||||
 | 
					- Compare Helper Methods
 | 
				
			||||||
 | 
					- InPlace manipulation
 | 
				
			||||||
 | 
					- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
 | 
				
			||||||
 | 
					- Wildcards `>=1.x`, `<=2.5.x`
 | 
				
			||||||
 | 
					- Sortable (implements sort.Interface)
 | 
				
			||||||
 | 
					- database/sql compatible (sql.Scanner/Valuer)
 | 
				
			||||||
 | 
					- encoding/json compatible (json.Marshaler/Unmarshaler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ranges
 | 
				
			||||||
 | 
					------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A `Range` is a set of conditions which specify which versions satisfy the range.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A condition is composed of an operator and a version. The supported operators are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `<1.0.0` Less than `1.0.0`
 | 
				
			||||||
 | 
					- `<=1.0.0` Less than or equal to `1.0.0`
 | 
				
			||||||
 | 
					- `>1.0.0` Greater than `1.0.0`
 | 
				
			||||||
 | 
					- `>=1.0.0` Greater than or equal to `1.0.0`
 | 
				
			||||||
 | 
					- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
 | 
				
			||||||
 | 
					- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that spaces between the operator and the version will be gracefully tolerated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A `Range` can link multiple `Ranges` separated by space:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ranges can be linked by logical AND:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
 | 
				
			||||||
 | 
					  - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ranges can also be linked by logical OR:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AND has a higher precedence than OR. It's not possible to use brackets.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ranges can be combined by both AND and OR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Range usage:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					v, err := semver.Parse("1.2.3")
 | 
				
			||||||
 | 
					range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
 | 
				
			||||||
 | 
					if range(v) {
 | 
				
			||||||
 | 
					    //valid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Have a look at full examples in [examples/main.go](examples/main.go)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					import github.com/blang/semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					v, err := semver.Make("0.0.1-alpha.preview+123.github")
 | 
				
			||||||
 | 
					fmt.Printf("Major: %d\n", v.Major)
 | 
				
			||||||
 | 
					fmt.Printf("Minor: %d\n", v.Minor)
 | 
				
			||||||
 | 
					fmt.Printf("Patch: %d\n", v.Patch)
 | 
				
			||||||
 | 
					fmt.Printf("Pre: %s\n", v.Pre)
 | 
				
			||||||
 | 
					fmt.Printf("Build: %s\n", v.Build)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Prerelease versions array
 | 
				
			||||||
 | 
					if len(v.Pre) > 0 {
 | 
				
			||||||
 | 
					    fmt.Println("Prerelease versions:")
 | 
				
			||||||
 | 
					    for i, pre := range v.Pre {
 | 
				
			||||||
 | 
					        fmt.Printf("%d: %q\n", i, pre)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Build meta data array
 | 
				
			||||||
 | 
					if len(v.Build) > 0 {
 | 
				
			||||||
 | 
					    fmt.Println("Build meta data:")
 | 
				
			||||||
 | 
					    for i, build := range v.Build {
 | 
				
			||||||
 | 
					        fmt.Printf("%d: %q\n", i, build)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					v001, err := semver.Make("0.0.1")
 | 
				
			||||||
 | 
					// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
 | 
				
			||||||
 | 
					v001.GT(v) == true
 | 
				
			||||||
 | 
					v.LT(v001) == true
 | 
				
			||||||
 | 
					v.GTE(v) == true
 | 
				
			||||||
 | 
					v.LTE(v) == true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Or use v.Compare(v2) for comparisons (-1, 0, 1):
 | 
				
			||||||
 | 
					v001.Compare(v) == 1
 | 
				
			||||||
 | 
					v.Compare(v001) == -1
 | 
				
			||||||
 | 
					v.Compare(v) == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Manipulate Version in place:
 | 
				
			||||||
 | 
					v.Pre[0], err = semver.NewPRVersion("beta")
 | 
				
			||||||
 | 
					if err != nil {
 | 
				
			||||||
 | 
					    fmt.Printf("Error parsing pre release version: %q", err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fmt.Println("\nValidate versions:")
 | 
				
			||||||
 | 
					v.Build[0] = "?"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err = v.Validate()
 | 
				
			||||||
 | 
					if err != nil {
 | 
				
			||||||
 | 
					    fmt.Printf("Validation failed: %s\n", err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Benchmarks
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BenchmarkParseSimple-4           5000000    390    ns/op    48 B/op   1 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkParseComplex-4          1000000   1813    ns/op   256 B/op   7 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkParseAverage-4          1000000   1171    ns/op   163 B/op   4 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkStringSimple-4         20000000    119    ns/op    16 B/op   1 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkStringLarger-4         10000000    206    ns/op    32 B/op   2 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkStringComplex-4         5000000    324    ns/op    80 B/op   3 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkStringAverage-4         5000000    273    ns/op    53 B/op   2 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkValidateSimple-4      200000000      9.33 ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkValidateComplex-4       3000000    469    ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkValidateAverage-4       5000000    256    ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkCompareSimple-4       100000000     11.8  ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkCompareComplex-4       50000000     30.8  ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkCompareAverage-4       30000000     41.5  ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkSort-4                  3000000    419    ns/op   256 B/op   2 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkRangeParseSimple-4      2000000    850    ns/op   192 B/op   5 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkRangeParseAverage-4     1000000   1677    ns/op   400 B/op  10 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkRangeParseComplex-4      300000   5214    ns/op  1440 B/op  30 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkRangeMatchSimple-4     50000000     25.6  ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkRangeMatchAverage-4    30000000     56.4  ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					    BenchmarkRangeMatchComplex-4    10000000    153    ns/op     0 B/op   0 allocs/op
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See benchmark cases at [semver_test.go](semver_test.go)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Motivation
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Contribution
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					License
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See [LICENSE](LICENSE) file.
 | 
				
			||||||
							
								
								
									
										23
									
								
								vendor/github.com/blang/semver/json.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/blang/semver/json.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MarshalJSON implements the encoding/json.Marshaler interface.
 | 
				
			||||||
 | 
					func (v Version) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						return json.Marshal(v.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
 | 
				
			||||||
 | 
					func (v *Version) UnmarshalJSON(data []byte) (err error) {
 | 
				
			||||||
 | 
						var versionString string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = json.Unmarshal(data, &versionString); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*v, err = Parse(versionString)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								vendor/github.com/blang/semver/package.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/blang/semver/package.json
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "author": "blang",
 | 
				
			||||||
 | 
					  "bugs": {
 | 
				
			||||||
 | 
					    "URL": "https://github.com/blang/semver/issues",
 | 
				
			||||||
 | 
					    "url": "https://github.com/blang/semver/issues"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "gx": {
 | 
				
			||||||
 | 
					    "dvcsimport": "github.com/blang/semver"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "gxVersion": "0.10.0",
 | 
				
			||||||
 | 
					  "language": "go",
 | 
				
			||||||
 | 
					  "license": "MIT",
 | 
				
			||||||
 | 
					  "name": "semver",
 | 
				
			||||||
 | 
					  "releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
 | 
				
			||||||
 | 
					  "version": "3.5.1"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										416
									
								
								vendor/github.com/blang/semver/range.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								vendor/github.com/blang/semver/range.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,416 @@
 | 
				
			|||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"unicode"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type wildcardType int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						noneWildcard  wildcardType = iota
 | 
				
			||||||
 | 
						majorWildcard wildcardType = 1
 | 
				
			||||||
 | 
						minorWildcard wildcardType = 2
 | 
				
			||||||
 | 
						patchWildcard wildcardType = 3
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func wildcardTypefromInt(i int) wildcardType {
 | 
				
			||||||
 | 
						switch i {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							return majorWildcard
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							return minorWildcard
 | 
				
			||||||
 | 
						case 3:
 | 
				
			||||||
 | 
							return patchWildcard
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return noneWildcard
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type comparator func(Version, Version) bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						compEQ comparator = func(v1 Version, v2 Version) bool {
 | 
				
			||||||
 | 
							return v1.Compare(v2) == 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						compNE = func(v1 Version, v2 Version) bool {
 | 
				
			||||||
 | 
							return v1.Compare(v2) != 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						compGT = func(v1 Version, v2 Version) bool {
 | 
				
			||||||
 | 
							return v1.Compare(v2) == 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						compGE = func(v1 Version, v2 Version) bool {
 | 
				
			||||||
 | 
							return v1.Compare(v2) >= 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						compLT = func(v1 Version, v2 Version) bool {
 | 
				
			||||||
 | 
							return v1.Compare(v2) == -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						compLE = func(v1 Version, v2 Version) bool {
 | 
				
			||||||
 | 
							return v1.Compare(v2) <= 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type versionRange struct {
 | 
				
			||||||
 | 
						v Version
 | 
				
			||||||
 | 
						c comparator
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// rangeFunc creates a Range from the given versionRange.
 | 
				
			||||||
 | 
					func (vr *versionRange) rangeFunc() Range {
 | 
				
			||||||
 | 
						return Range(func(v Version) bool {
 | 
				
			||||||
 | 
							return vr.c(v, vr.v)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Range represents a range of versions.
 | 
				
			||||||
 | 
					// A Range can be used to check if a Version satisfies it:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     range, err := semver.ParseRange(">1.0.0 <2.0.0")
 | 
				
			||||||
 | 
					//     range(semver.MustParse("1.1.1") // returns true
 | 
				
			||||||
 | 
					type Range func(Version) bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OR combines the existing Range with another Range using logical OR.
 | 
				
			||||||
 | 
					func (rf Range) OR(f Range) Range {
 | 
				
			||||||
 | 
						return Range(func(v Version) bool {
 | 
				
			||||||
 | 
							return rf(v) || f(v)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AND combines the existing Range with another Range using logical AND.
 | 
				
			||||||
 | 
					func (rf Range) AND(f Range) Range {
 | 
				
			||||||
 | 
						return Range(func(v Version) bool {
 | 
				
			||||||
 | 
							return rf(v) && f(v)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParseRange parses a range and returns a Range.
 | 
				
			||||||
 | 
					// If the range could not be parsed an error is returned.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Valid ranges are:
 | 
				
			||||||
 | 
					//   - "<1.0.0"
 | 
				
			||||||
 | 
					//   - "<=1.0.0"
 | 
				
			||||||
 | 
					//   - ">1.0.0"
 | 
				
			||||||
 | 
					//   - ">=1.0.0"
 | 
				
			||||||
 | 
					//   - "1.0.0", "=1.0.0", "==1.0.0"
 | 
				
			||||||
 | 
					//   - "!1.0.0", "!=1.0.0"
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// A Range can consist of multiple ranges separated by space:
 | 
				
			||||||
 | 
					// Ranges can be linked by logical AND:
 | 
				
			||||||
 | 
					//   - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
 | 
				
			||||||
 | 
					//   - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Ranges can also be linked by logical OR:
 | 
				
			||||||
 | 
					//   - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// AND has a higher precedence than OR. It's not possible to use brackets.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Ranges can be combined by both AND and OR
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
 | 
				
			||||||
 | 
					func ParseRange(s string) (Range, error) {
 | 
				
			||||||
 | 
						parts := splitAndTrim(s)
 | 
				
			||||||
 | 
						orParts, err := splitORParts(parts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						expandedParts, err := expandWildcardVersion(orParts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var orFn Range
 | 
				
			||||||
 | 
						for _, p := range expandedParts {
 | 
				
			||||||
 | 
							var andFn Range
 | 
				
			||||||
 | 
							for _, ap := range p {
 | 
				
			||||||
 | 
								opStr, vStr, err := splitComparatorVersion(ap)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								vr, err := buildVersionRange(opStr, vStr)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								rf := vr.rangeFunc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Set function
 | 
				
			||||||
 | 
								if andFn == nil {
 | 
				
			||||||
 | 
									andFn = rf
 | 
				
			||||||
 | 
								} else { // Combine with existing function
 | 
				
			||||||
 | 
									andFn = andFn.AND(rf)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if orFn == nil {
 | 
				
			||||||
 | 
								orFn = andFn
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								orFn = orFn.OR(andFn)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return orFn, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// splitORParts splits the already cleaned parts by '||'.
 | 
				
			||||||
 | 
					// Checks for invalid positions of the operator and returns an
 | 
				
			||||||
 | 
					// error if found.
 | 
				
			||||||
 | 
					func splitORParts(parts []string) ([][]string, error) {
 | 
				
			||||||
 | 
						var ORparts [][]string
 | 
				
			||||||
 | 
						last := 0
 | 
				
			||||||
 | 
						for i, p := range parts {
 | 
				
			||||||
 | 
							if p == "||" {
 | 
				
			||||||
 | 
								if i == 0 {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("First element in range is '||'")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ORparts = append(ORparts, parts[last:i])
 | 
				
			||||||
 | 
								last = i + 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if last == len(parts) {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("Last element in range is '||'")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ORparts = append(ORparts, parts[last:])
 | 
				
			||||||
 | 
						return ORparts, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// buildVersionRange takes a slice of 2: operator and version
 | 
				
			||||||
 | 
					// and builds a versionRange, otherwise an error.
 | 
				
			||||||
 | 
					func buildVersionRange(opStr, vStr string) (*versionRange, error) {
 | 
				
			||||||
 | 
						c := parseComparator(opStr)
 | 
				
			||||||
 | 
						if c == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						v, err := Parse(vStr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &versionRange{
 | 
				
			||||||
 | 
							v: v,
 | 
				
			||||||
 | 
							c: c,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// inArray checks if a byte is contained in an array of bytes
 | 
				
			||||||
 | 
					func inArray(s byte, list []byte) bool {
 | 
				
			||||||
 | 
						for _, el := range list {
 | 
				
			||||||
 | 
							if el == s {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// splitAndTrim splits a range string by spaces and cleans whitespaces
 | 
				
			||||||
 | 
					func splitAndTrim(s string) (result []string) {
 | 
				
			||||||
 | 
						last := 0
 | 
				
			||||||
 | 
						var lastChar byte
 | 
				
			||||||
 | 
						excludeFromSplit := []byte{'>', '<', '='}
 | 
				
			||||||
 | 
						for i := 0; i < len(s); i++ {
 | 
				
			||||||
 | 
							if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
 | 
				
			||||||
 | 
								if last < i-1 {
 | 
				
			||||||
 | 
									result = append(result, s[last:i])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								last = i + 1
 | 
				
			||||||
 | 
							} else if s[i] != ' ' {
 | 
				
			||||||
 | 
								lastChar = s[i]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if last < len(s)-1 {
 | 
				
			||||||
 | 
							result = append(result, s[last:])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, v := range result {
 | 
				
			||||||
 | 
							result[i] = strings.Replace(v, " ", "", -1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// parts := strings.Split(s, " ")
 | 
				
			||||||
 | 
						// for _, x := range parts {
 | 
				
			||||||
 | 
						// 	if s := strings.TrimSpace(x); len(s) != 0 {
 | 
				
			||||||
 | 
						// 		result = append(result, s)
 | 
				
			||||||
 | 
						// 	}
 | 
				
			||||||
 | 
						// }
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// splitComparatorVersion splits the comparator from the version.
 | 
				
			||||||
 | 
					// Input must be free of leading or trailing spaces.
 | 
				
			||||||
 | 
					func splitComparatorVersion(s string) (string, string, error) {
 | 
				
			||||||
 | 
						i := strings.IndexFunc(s, unicode.IsDigit)
 | 
				
			||||||
 | 
						if i == -1 {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("Could not get version from string: %q", s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.TrimSpace(s[0:i]), s[i:], nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getWildcardType will return the type of wildcard that the
 | 
				
			||||||
 | 
					// passed version contains
 | 
				
			||||||
 | 
					func getWildcardType(vStr string) wildcardType {
 | 
				
			||||||
 | 
						parts := strings.Split(vStr, ".")
 | 
				
			||||||
 | 
						nparts := len(parts)
 | 
				
			||||||
 | 
						wildcard := parts[nparts-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						possibleWildcardType := wildcardTypefromInt(nparts)
 | 
				
			||||||
 | 
						if wildcard == "x" {
 | 
				
			||||||
 | 
							return possibleWildcardType
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return noneWildcard
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// createVersionFromWildcard will convert a wildcard version
 | 
				
			||||||
 | 
					// into a regular version, replacing 'x's with '0's, handling
 | 
				
			||||||
 | 
					// special cases like '1.x.x' and '1.x'
 | 
				
			||||||
 | 
					func createVersionFromWildcard(vStr string) string {
 | 
				
			||||||
 | 
						// handle 1.x.x
 | 
				
			||||||
 | 
						vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
 | 
				
			||||||
 | 
						vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
 | 
				
			||||||
 | 
						parts := strings.Split(vStr2, ".")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// handle 1.x
 | 
				
			||||||
 | 
						if len(parts) == 2 {
 | 
				
			||||||
 | 
							return vStr2 + ".0"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return vStr2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// incrementMajorVersion will increment the major version
 | 
				
			||||||
 | 
					// of the passed version
 | 
				
			||||||
 | 
					func incrementMajorVersion(vStr string) (string, error) {
 | 
				
			||||||
 | 
						parts := strings.Split(vStr, ".")
 | 
				
			||||||
 | 
						i, err := strconv.Atoi(parts[0])
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						parts[0] = strconv.Itoa(i + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return strings.Join(parts, "."), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// incrementMajorVersion will increment the minor version
 | 
				
			||||||
 | 
					// of the passed version
 | 
				
			||||||
 | 
					func incrementMinorVersion(vStr string) (string, error) {
 | 
				
			||||||
 | 
						parts := strings.Split(vStr, ".")
 | 
				
			||||||
 | 
						i, err := strconv.Atoi(parts[1])
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						parts[1] = strconv.Itoa(i + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return strings.Join(parts, "."), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// expandWildcardVersion will expand wildcards inside versions
 | 
				
			||||||
 | 
					// following these rules:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// * when dealing with patch wildcards:
 | 
				
			||||||
 | 
					// >= 1.2.x    will become    >= 1.2.0
 | 
				
			||||||
 | 
					// <= 1.2.x    will become    <  1.3.0
 | 
				
			||||||
 | 
					// >  1.2.x    will become    >= 1.3.0
 | 
				
			||||||
 | 
					// <  1.2.x    will become    <  1.2.0
 | 
				
			||||||
 | 
					// != 1.2.x    will become    <  1.2.0 >= 1.3.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// * when dealing with minor wildcards:
 | 
				
			||||||
 | 
					// >= 1.x      will become    >= 1.0.0
 | 
				
			||||||
 | 
					// <= 1.x      will become    <  2.0.0
 | 
				
			||||||
 | 
					// >  1.x      will become    >= 2.0.0
 | 
				
			||||||
 | 
					// <  1.0      will become    <  1.0.0
 | 
				
			||||||
 | 
					// != 1.x      will become    <  1.0.0 >= 2.0.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// * when dealing with wildcards without
 | 
				
			||||||
 | 
					// version operator:
 | 
				
			||||||
 | 
					// 1.2.x       will become    >= 1.2.0 < 1.3.0
 | 
				
			||||||
 | 
					// 1.x         will become    >= 1.0.0 < 2.0.0
 | 
				
			||||||
 | 
					func expandWildcardVersion(parts [][]string) ([][]string, error) {
 | 
				
			||||||
 | 
						var expandedParts [][]string
 | 
				
			||||||
 | 
						for _, p := range parts {
 | 
				
			||||||
 | 
							var newParts []string
 | 
				
			||||||
 | 
							for _, ap := range p {
 | 
				
			||||||
 | 
								if strings.Index(ap, "x") != -1 {
 | 
				
			||||||
 | 
									opStr, vStr, err := splitComparatorVersion(ap)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return nil, err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									versionWildcardType := getWildcardType(vStr)
 | 
				
			||||||
 | 
									flatVersion := createVersionFromWildcard(vStr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									var resultOperator string
 | 
				
			||||||
 | 
									var shouldIncrementVersion bool
 | 
				
			||||||
 | 
									switch opStr {
 | 
				
			||||||
 | 
									case ">":
 | 
				
			||||||
 | 
										resultOperator = ">="
 | 
				
			||||||
 | 
										shouldIncrementVersion = true
 | 
				
			||||||
 | 
									case ">=":
 | 
				
			||||||
 | 
										resultOperator = ">="
 | 
				
			||||||
 | 
									case "<":
 | 
				
			||||||
 | 
										resultOperator = "<"
 | 
				
			||||||
 | 
									case "<=":
 | 
				
			||||||
 | 
										resultOperator = "<"
 | 
				
			||||||
 | 
										shouldIncrementVersion = true
 | 
				
			||||||
 | 
									case "", "=", "==":
 | 
				
			||||||
 | 
										newParts = append(newParts, ">="+flatVersion)
 | 
				
			||||||
 | 
										resultOperator = "<"
 | 
				
			||||||
 | 
										shouldIncrementVersion = true
 | 
				
			||||||
 | 
									case "!=", "!":
 | 
				
			||||||
 | 
										newParts = append(newParts, "<"+flatVersion)
 | 
				
			||||||
 | 
										resultOperator = ">="
 | 
				
			||||||
 | 
										shouldIncrementVersion = true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									var resultVersion string
 | 
				
			||||||
 | 
									if shouldIncrementVersion {
 | 
				
			||||||
 | 
										switch versionWildcardType {
 | 
				
			||||||
 | 
										case patchWildcard:
 | 
				
			||||||
 | 
											resultVersion, _ = incrementMinorVersion(flatVersion)
 | 
				
			||||||
 | 
										case minorWildcard:
 | 
				
			||||||
 | 
											resultVersion, _ = incrementMajorVersion(flatVersion)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										resultVersion = flatVersion
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ap = resultOperator + resultVersion
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								newParts = append(newParts, ap)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							expandedParts = append(expandedParts, newParts)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return expandedParts, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseComparator(s string) comparator {
 | 
				
			||||||
 | 
						switch s {
 | 
				
			||||||
 | 
						case "==":
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case "":
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case "=":
 | 
				
			||||||
 | 
							return compEQ
 | 
				
			||||||
 | 
						case ">":
 | 
				
			||||||
 | 
							return compGT
 | 
				
			||||||
 | 
						case ">=":
 | 
				
			||||||
 | 
							return compGE
 | 
				
			||||||
 | 
						case "<":
 | 
				
			||||||
 | 
							return compLT
 | 
				
			||||||
 | 
						case "<=":
 | 
				
			||||||
 | 
							return compLE
 | 
				
			||||||
 | 
						case "!":
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case "!=":
 | 
				
			||||||
 | 
							return compNE
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MustParseRange is like ParseRange but panics if the range cannot be parsed.
 | 
				
			||||||
 | 
					func MustParseRange(s string) Range {
 | 
				
			||||||
 | 
						r, err := ParseRange(s)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(`semver: ParseRange(` + s + `): ` + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										418
									
								
								vendor/github.com/blang/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								vendor/github.com/blang/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,418 @@
 | 
				
			|||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						numbers  string = "0123456789"
 | 
				
			||||||
 | 
						alphas          = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
 | 
				
			||||||
 | 
						alphanum        = alphas + numbers
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SpecVersion is the latest fully supported spec version of semver
 | 
				
			||||||
 | 
					var SpecVersion = Version{
 | 
				
			||||||
 | 
						Major: 2,
 | 
				
			||||||
 | 
						Minor: 0,
 | 
				
			||||||
 | 
						Patch: 0,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Version represents a semver compatible version
 | 
				
			||||||
 | 
					type Version struct {
 | 
				
			||||||
 | 
						Major uint64
 | 
				
			||||||
 | 
						Minor uint64
 | 
				
			||||||
 | 
						Patch uint64
 | 
				
			||||||
 | 
						Pre   []PRVersion
 | 
				
			||||||
 | 
						Build []string //No Precendence
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Version to string
 | 
				
			||||||
 | 
					func (v Version) String() string {
 | 
				
			||||||
 | 
						b := make([]byte, 0, 5)
 | 
				
			||||||
 | 
						b = strconv.AppendUint(b, v.Major, 10)
 | 
				
			||||||
 | 
						b = append(b, '.')
 | 
				
			||||||
 | 
						b = strconv.AppendUint(b, v.Minor, 10)
 | 
				
			||||||
 | 
						b = append(b, '.')
 | 
				
			||||||
 | 
						b = strconv.AppendUint(b, v.Patch, 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(v.Pre) > 0 {
 | 
				
			||||||
 | 
							b = append(b, '-')
 | 
				
			||||||
 | 
							b = append(b, v.Pre[0].String()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, pre := range v.Pre[1:] {
 | 
				
			||||||
 | 
								b = append(b, '.')
 | 
				
			||||||
 | 
								b = append(b, pre.String()...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(v.Build) > 0 {
 | 
				
			||||||
 | 
							b = append(b, '+')
 | 
				
			||||||
 | 
							b = append(b, v.Build[0]...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, build := range v.Build[1:] {
 | 
				
			||||||
 | 
								b = append(b, '.')
 | 
				
			||||||
 | 
								b = append(b, build...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return string(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Equals checks if v is equal to o.
 | 
				
			||||||
 | 
					func (v Version) Equals(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) == 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EQ checks if v is equal to o.
 | 
				
			||||||
 | 
					func (v Version) EQ(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) == 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NE checks if v is not equal to o.
 | 
				
			||||||
 | 
					func (v Version) NE(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) != 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GT checks if v is greater than o.
 | 
				
			||||||
 | 
					func (v Version) GT(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) == 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GTE checks if v is greater than or equal to o.
 | 
				
			||||||
 | 
					func (v Version) GTE(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) >= 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GE checks if v is greater than or equal to o.
 | 
				
			||||||
 | 
					func (v Version) GE(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) >= 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LT checks if v is less than o.
 | 
				
			||||||
 | 
					func (v Version) LT(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) == -1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LTE checks if v is less than or equal to o.
 | 
				
			||||||
 | 
					func (v Version) LTE(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) <= 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LE checks if v is less than or equal to o.
 | 
				
			||||||
 | 
					func (v Version) LE(o Version) bool {
 | 
				
			||||||
 | 
						return (v.Compare(o) <= 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compare compares Versions v to o:
 | 
				
			||||||
 | 
					// -1 == v is less than o
 | 
				
			||||||
 | 
					// 0 == v is equal to o
 | 
				
			||||||
 | 
					// 1 == v is greater than o
 | 
				
			||||||
 | 
					func (v Version) Compare(o Version) int {
 | 
				
			||||||
 | 
						if v.Major != o.Major {
 | 
				
			||||||
 | 
							if v.Major > o.Major {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v.Minor != o.Minor {
 | 
				
			||||||
 | 
							if v.Minor > o.Minor {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v.Patch != o.Patch {
 | 
				
			||||||
 | 
							if v.Patch > o.Patch {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Quick comparison if a version has no prerelease versions
 | 
				
			||||||
 | 
						if len(v.Pre) == 0 && len(o.Pre) == 0 {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i := 0
 | 
				
			||||||
 | 
						for ; i < len(v.Pre) && i < len(o.Pre); i++ {
 | 
				
			||||||
 | 
							if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							} else if comp == 1 {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If all pr versions are the equal but one has further prversion, this one greater
 | 
				
			||||||
 | 
						if i == len(v.Pre) && i == len(o.Pre) {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						} else if i == len(v.Pre) && i < len(o.Pre) {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate validates v and returns error in case
 | 
				
			||||||
 | 
					func (v Version) Validate() error {
 | 
				
			||||||
 | 
						// Major, Minor, Patch already validated using uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, pre := range v.Pre {
 | 
				
			||||||
 | 
							if !pre.IsNum { //Numeric prerelease versions already uint64
 | 
				
			||||||
 | 
								if len(pre.VersionStr) == 0 {
 | 
				
			||||||
 | 
									return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !containsOnly(pre.VersionStr, alphanum) {
 | 
				
			||||||
 | 
									return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, build := range v.Build {
 | 
				
			||||||
 | 
							if len(build) == 0 {
 | 
				
			||||||
 | 
								return fmt.Errorf("Build meta data can not be empty %q", build)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !containsOnly(build, alphanum) {
 | 
				
			||||||
 | 
								return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
 | 
				
			||||||
 | 
					func New(s string) (vp *Version, err error) {
 | 
				
			||||||
 | 
						v, err := Parse(s)
 | 
				
			||||||
 | 
						vp = &v
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make is an alias for Parse, parses version string and returns a validated Version or error
 | 
				
			||||||
 | 
					func Make(s string) (Version, error) {
 | 
				
			||||||
 | 
						return Parse(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
 | 
				
			||||||
 | 
					// specs to be parsed by this library. It does so by normalizing versions before passing them to
 | 
				
			||||||
 | 
					// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
 | 
				
			||||||
 | 
					// with only major and minor components specified
 | 
				
			||||||
 | 
					func ParseTolerant(s string) (Version, error) {
 | 
				
			||||||
 | 
						s = strings.TrimSpace(s)
 | 
				
			||||||
 | 
						s = strings.TrimPrefix(s, "v")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Split into major.minor.(patch+pr+meta)
 | 
				
			||||||
 | 
						parts := strings.SplitN(s, ".", 3)
 | 
				
			||||||
 | 
						if len(parts) < 3 {
 | 
				
			||||||
 | 
							if strings.ContainsAny(parts[len(parts)-1], "+-") {
 | 
				
			||||||
 | 
								return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for len(parts) < 3 {
 | 
				
			||||||
 | 
								parts = append(parts, "0")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s = strings.Join(parts, ".")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Parse(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parse parses version string and returns a validated Version or error
 | 
				
			||||||
 | 
					func Parse(s string) (Version, error) {
 | 
				
			||||||
 | 
						if len(s) == 0 {
 | 
				
			||||||
 | 
							return Version{}, errors.New("Version string empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Split into major.minor.(patch+pr+meta)
 | 
				
			||||||
 | 
						parts := strings.SplitN(s, ".", 3)
 | 
				
			||||||
 | 
						if len(parts) != 3 {
 | 
				
			||||||
 | 
							return Version{}, errors.New("No Major.Minor.Patch elements found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Major
 | 
				
			||||||
 | 
						if !containsOnly(parts[0], numbers) {
 | 
				
			||||||
 | 
							return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if hasLeadingZeroes(parts[0]) {
 | 
				
			||||||
 | 
							return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						major, err := strconv.ParseUint(parts[0], 10, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return Version{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Minor
 | 
				
			||||||
 | 
						if !containsOnly(parts[1], numbers) {
 | 
				
			||||||
 | 
							return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if hasLeadingZeroes(parts[1]) {
 | 
				
			||||||
 | 
							return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						minor, err := strconv.ParseUint(parts[1], 10, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return Version{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v := Version{}
 | 
				
			||||||
 | 
						v.Major = major
 | 
				
			||||||
 | 
						v.Minor = minor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var build, prerelease []string
 | 
				
			||||||
 | 
						patchStr := parts[2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
 | 
				
			||||||
 | 
							build = strings.Split(patchStr[buildIndex+1:], ".")
 | 
				
			||||||
 | 
							patchStr = patchStr[:buildIndex]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
 | 
				
			||||||
 | 
							prerelease = strings.Split(patchStr[preIndex+1:], ".")
 | 
				
			||||||
 | 
							patchStr = patchStr[:preIndex]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !containsOnly(patchStr, numbers) {
 | 
				
			||||||
 | 
							return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if hasLeadingZeroes(patchStr) {
 | 
				
			||||||
 | 
							return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						patch, err := strconv.ParseUint(patchStr, 10, 64)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return Version{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v.Patch = patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Prerelease
 | 
				
			||||||
 | 
						for _, prstr := range prerelease {
 | 
				
			||||||
 | 
							parsedPR, err := NewPRVersion(prstr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return Version{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v.Pre = append(v.Pre, parsedPR)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Build meta data
 | 
				
			||||||
 | 
						for _, str := range build {
 | 
				
			||||||
 | 
							if len(str) == 0 {
 | 
				
			||||||
 | 
								return Version{}, errors.New("Build meta data is empty")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !containsOnly(str, alphanum) {
 | 
				
			||||||
 | 
								return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v.Build = append(v.Build, str)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return v, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MustParse is like Parse but panics if the version cannot be parsed.
 | 
				
			||||||
 | 
					func MustParse(s string) Version {
 | 
				
			||||||
 | 
						v, err := Parse(s)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(`semver: Parse(` + s + `): ` + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PRVersion represents a PreRelease Version
 | 
				
			||||||
 | 
					type PRVersion struct {
 | 
				
			||||||
 | 
						VersionStr string
 | 
				
			||||||
 | 
						VersionNum uint64
 | 
				
			||||||
 | 
						IsNum      bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewPRVersion creates a new valid prerelease version
 | 
				
			||||||
 | 
					func NewPRVersion(s string) (PRVersion, error) {
 | 
				
			||||||
 | 
						if len(s) == 0 {
 | 
				
			||||||
 | 
							return PRVersion{}, errors.New("Prerelease is empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						v := PRVersion{}
 | 
				
			||||||
 | 
						if containsOnly(s, numbers) {
 | 
				
			||||||
 | 
							if hasLeadingZeroes(s) {
 | 
				
			||||||
 | 
								return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							num, err := strconv.ParseUint(s, 10, 64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Might never be hit, but just in case
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return PRVersion{}, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v.VersionNum = num
 | 
				
			||||||
 | 
							v.IsNum = true
 | 
				
			||||||
 | 
						} else if containsOnly(s, alphanum) {
 | 
				
			||||||
 | 
							v.VersionStr = s
 | 
				
			||||||
 | 
							v.IsNum = false
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsNumeric checks if prerelease-version is numeric
 | 
				
			||||||
 | 
					func (v PRVersion) IsNumeric() bool {
 | 
				
			||||||
 | 
						return v.IsNum
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Compare compares two PreRelease Versions v and o:
 | 
				
			||||||
 | 
					// -1 == v is less than o
 | 
				
			||||||
 | 
					// 0 == v is equal to o
 | 
				
			||||||
 | 
					// 1 == v is greater than o
 | 
				
			||||||
 | 
					func (v PRVersion) Compare(o PRVersion) int {
 | 
				
			||||||
 | 
						if v.IsNum && !o.IsNum {
 | 
				
			||||||
 | 
							return -1
 | 
				
			||||||
 | 
						} else if !v.IsNum && o.IsNum {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						} else if v.IsNum && o.IsNum {
 | 
				
			||||||
 | 
							if v.VersionNum == o.VersionNum {
 | 
				
			||||||
 | 
								return 0
 | 
				
			||||||
 | 
							} else if v.VersionNum > o.VersionNum {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else { // both are Alphas
 | 
				
			||||||
 | 
							if v.VersionStr == o.VersionStr {
 | 
				
			||||||
 | 
								return 0
 | 
				
			||||||
 | 
							} else if v.VersionStr > o.VersionStr {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PreRelease version to string
 | 
				
			||||||
 | 
					func (v PRVersion) String() string {
 | 
				
			||||||
 | 
						if v.IsNum {
 | 
				
			||||||
 | 
							return strconv.FormatUint(v.VersionNum, 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v.VersionStr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func containsOnly(s string, set string) bool {
 | 
				
			||||||
 | 
						return strings.IndexFunc(s, func(r rune) bool {
 | 
				
			||||||
 | 
							return !strings.ContainsRune(set, r)
 | 
				
			||||||
 | 
						}) == -1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func hasLeadingZeroes(s string) bool {
 | 
				
			||||||
 | 
						return len(s) > 1 && s[0] == '0'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewBuildVersion creates a new valid build version
 | 
				
			||||||
 | 
					func NewBuildVersion(s string) (string, error) {
 | 
				
			||||||
 | 
						if len(s) == 0 {
 | 
				
			||||||
 | 
							return "", errors.New("Buildversion is empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !containsOnly(s, alphanum) {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								vendor/github.com/blang/semver/sort.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/blang/semver/sort.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Versions represents multiple versions.
 | 
				
			||||||
 | 
					type Versions []Version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Len returns length of version collection
 | 
				
			||||||
 | 
					func (s Versions) Len() int {
 | 
				
			||||||
 | 
						return len(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Swap swaps two versions inside the collection by its indices
 | 
				
			||||||
 | 
					func (s Versions) Swap(i, j int) {
 | 
				
			||||||
 | 
						s[i], s[j] = s[j], s[i]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Less checks if version at index i is less than version at index j
 | 
				
			||||||
 | 
					func (s Versions) Less(i, j int) bool {
 | 
				
			||||||
 | 
						return s[i].LT(s[j])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sort sorts a slice of versions
 | 
				
			||||||
 | 
					func Sort(versions []Version) {
 | 
				
			||||||
 | 
						sort.Sort(Versions(versions))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								vendor/github.com/blang/semver/sql.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/blang/semver/sql.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package semver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql/driver"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Scan implements the database/sql.Scanner interface.
 | 
				
			||||||
 | 
					func (v *Version) Scan(src interface{}) (err error) {
 | 
				
			||||||
 | 
						var str string
 | 
				
			||||||
 | 
						switch src := src.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							str = src
 | 
				
			||||||
 | 
						case []byte:
 | 
				
			||||||
 | 
							str = string(src)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if t, err := Parse(str); err == nil {
 | 
				
			||||||
 | 
							*v = t
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Value implements the database/sql/driver.Valuer interface.
 | 
				
			||||||
 | 
					func (v Version) Value() (driver.Value, error) {
 | 
				
			||||||
 | 
						return v.String(), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								vendor/github.com/bwmarrin/discordgo/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/bwmarrin/discordgo/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,6 +0,0 @@
 | 
				
			|||||||
module github.com/bwmarrin/discordgo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
require (
 | 
					 | 
				
			||||||
	github.com/gorilla/websocket v1.4.0
 | 
					 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
							
								
								
									
										4
									
								
								vendor/github.com/bwmarrin/discordgo/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/bwmarrin/discordgo/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,4 +0,0 @@
 | 
				
			|||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
					 | 
				
			||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
					 | 
				
			||||||
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=
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								vendor/github.com/d5/tengo/v2/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/d5/tengo/v2/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,9 +11,10 @@ builds:
 | 
				
			|||||||
      - darwin
 | 
					      - darwin
 | 
				
			||||||
      - linux
 | 
					      - linux
 | 
				
			||||||
      - windows
 | 
					      - windows
 | 
				
			||||||
archive:
 | 
					archives:
 | 
				
			||||||
  files:
 | 
					  -
 | 
				
			||||||
    - none*
 | 
					    files:
 | 
				
			||||||
 | 
					      - none*
 | 
				
			||||||
checksum:
 | 
					checksum:
 | 
				
			||||||
  name_template: 'checksums.txt'
 | 
					  name_template: 'checksums.txt'
 | 
				
			||||||
changelog:
 | 
					changelog:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								vendor/github.com/d5/tengo/v2/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/d5/tengo/v2/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,6 +6,7 @@ lint:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
test: generate lint
 | 
					test: generate lint
 | 
				
			||||||
	go test -race -cover ./...
 | 
						go test -race -cover ./...
 | 
				
			||||||
 | 
						go run ./cmd/tengo -resolve ./testdata/cli/test.tengo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fmt:
 | 
					fmt:
 | 
				
			||||||
	go fmt ./...
 | 
						go fmt ./...
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,9 +5,8 @@
 | 
				
			|||||||
# The Tengo Language
 | 
					# The Tengo Language
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://godoc.org/github.com/d5/tengo)
 | 
					[](https://godoc.org/github.com/d5/tengo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://goreportcard.com/report/github.com/d5/tengo)
 | 
					[](https://goreportcard.com/report/github.com/d5/tengo)
 | 
				
			||||||
[](https://circleci.com/gh/d5/tengo)
 | 
					 | 
				
			||||||
[](https://sourcegraph.com/github.com/d5/tengo?badge)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Tengo is a small, dynamic, fast, secure script language for Go.** 
 | 
					**Tengo is a small, dynamic, fast, secure script language for Go.** 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,19 +51,21 @@ fmt.println(sum("", [1, 2, 3]))  // "123"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Benchmark
 | 
					## Benchmark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| | fib(35) | fibt(35) |  Type  |
 | 
					| | fib(35) | fibt(35) |  Language (Type)  |
 | 
				
			||||||
| :--- |    ---: |     ---: |  :---: |
 | 
					| :--- |    ---: |     ---: |  :---: |
 | 
				
			||||||
| Go | `48ms` | `3ms` | Go (native) |
 | 
					| [**Tengo**](https://github.com/d5/tengo) | `2,931ms` | `4ms` | Tengo (VM) |
 | 
				
			||||||
| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go |
 | 
					| [go-lua](https://github.com/Shopify/go-lua) | `4,824ms` | `4ms` | Lua (VM) |
 | 
				
			||||||
| Lua | `1,416ms` | `3ms` | Lua (native) |
 | 
					| [GopherLua](https://github.com/yuin/gopher-lua) | `5,365ms` | `4ms` | Lua (VM) |
 | 
				
			||||||
| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go |
 | 
					| [goja](https://github.com/dop251/goja) | `5,533ms` | `5ms` | JavaScript (VM) |
 | 
				
			||||||
| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go |
 | 
					| [starlark-go](https://github.com/google/starlark-go) | `11,495ms` | `5ms` | Starlark (Interpreter) |
 | 
				
			||||||
| Python | `2,588ms` | `26ms` | Python (native) |
 | 
					| [Yaegi](https://github.com/containous/yaegi) | `15,645ms` | `12ms` | Yaegi (Interpreter) |
 | 
				
			||||||
| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go |
 | 
					| [gpython](https://github.com/go-python/gpython) | `16,322ms` | `5ms` | Python (Interpreter) |
 | 
				
			||||||
| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go |
 | 
					| [otto](https://github.com/robertkrimen/otto) | `73,093ms` | `10ms` | JavaScript (Interpreter) |
 | 
				
			||||||
| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go |
 | 
					| [Anko](https://github.com/mattn/anko) | `79,809ms` | `8ms` | Anko (Interpreter) |
 | 
				
			||||||
| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go |
 | 
					| - | - | - | - |
 | 
				
			||||||
| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go |
 | 
					| Go | `53ms` | `3ms` | Go (Native) |
 | 
				
			||||||
 | 
					| Lua | `1,612ms` | `3ms` | Lua (Native) |
 | 
				
			||||||
 | 
					| Python | `2,632ms` | `23ms` | Python 2 (Native) |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo):
 | 
					_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo):
 | 
				
			||||||
Fibonacci(35)_  
 | 
					Fibonacci(35)_  
 | 
				
			||||||
@@ -75,6 +76,10 @@ _* See [here](https://github.com/d5/tengobench) for commands/codes used_
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Quick Start
 | 
					## Quick Start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					go get github.com/d5/tengo/v2
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A simple Go example code that compiles/runs Tengo script code with some input/output values:
 | 
					A simple Go example code that compiles/runs Tengo script code with some input/output values:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```golang
 | 
					```golang
 | 
				
			||||||
@@ -133,3 +138,10 @@ each([a, b, c, d], func(x) {
 | 
				
			|||||||
- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
 | 
					- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
 | 
				
			||||||
- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
 | 
					- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
 | 
				
			||||||
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
 | 
					- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
 | 
				
			||||||
 | 
					- Syntax Highlighters: [VSCode](https://github.com/lissein/vscode-tengo), [Atom](https://github.com/d5/tengo-atom)
 | 
				
			||||||
 | 
					- **Why the name Tengo?** It's from [1Q84](https://en.wikipedia.org/wiki/1Q84).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:hearts: Like writing Go code? Come work at Skool. [We're hiring!](https://jobs.lever.co/skool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										109
									
								
								vendor/github.com/d5/tengo/v2/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/d5/tengo/v2/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,14 @@ var builtinFuncs = []*BuiltinFunction{
 | 
				
			|||||||
		Name:  "append",
 | 
							Name:  "append",
 | 
				
			||||||
		Value: builtinAppend,
 | 
							Value: builtinAppend,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Name:  "delete",
 | 
				
			||||||
 | 
							Value: builtinDelete,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Name:  "splice",
 | 
				
			||||||
 | 
							Value: builtinSplice,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Name:  "string",
 | 
							Name:  "string",
 | 
				
			||||||
		Value: builtinString,
 | 
							Value: builtinString,
 | 
				
			||||||
@@ -500,3 +508,104 @@ func builtinAppend(args ...Object) (Object, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// builtinDelete deletes Map keys
 | 
				
			||||||
 | 
					// usage: delete(map, "key")
 | 
				
			||||||
 | 
					// key must be a string
 | 
				
			||||||
 | 
					func builtinDelete(args ...Object) (Object, error) {
 | 
				
			||||||
 | 
						argsLen := len(args)
 | 
				
			||||||
 | 
						if argsLen != 2 {
 | 
				
			||||||
 | 
							return nil, ErrWrongNumArguments
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch arg := args[0].(type) {
 | 
				
			||||||
 | 
						case *Map:
 | 
				
			||||||
 | 
							if key, ok := args[1].(*String); ok {
 | 
				
			||||||
 | 
								delete(arg.Value, key.Value)
 | 
				
			||||||
 | 
								return UndefinedValue, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, ErrInvalidArgumentType{
 | 
				
			||||||
 | 
								Name:     "second",
 | 
				
			||||||
 | 
								Expected: "string",
 | 
				
			||||||
 | 
								Found:    args[1].TypeName(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, ErrInvalidArgumentType{
 | 
				
			||||||
 | 
								Name:     "first",
 | 
				
			||||||
 | 
								Expected: "map",
 | 
				
			||||||
 | 
								Found:    arg.TypeName(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// builtinSplice deletes and changes given Array, returns deleted items.
 | 
				
			||||||
 | 
					// usage:
 | 
				
			||||||
 | 
					// deleted_items := splice(array[,start[,delete_count[,item1[,item2[,...]]]])
 | 
				
			||||||
 | 
					func builtinSplice(args ...Object) (Object, error) {
 | 
				
			||||||
 | 
						argsLen := len(args)
 | 
				
			||||||
 | 
						if argsLen == 0 {
 | 
				
			||||||
 | 
							return nil, ErrWrongNumArguments
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						array, ok := args[0].(*Array)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, ErrInvalidArgumentType{
 | 
				
			||||||
 | 
								Name:     "first",
 | 
				
			||||||
 | 
								Expected: "array",
 | 
				
			||||||
 | 
								Found:    args[0].TypeName(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						arrayLen := len(array.Value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var startIdx int
 | 
				
			||||||
 | 
						if argsLen > 1 {
 | 
				
			||||||
 | 
							arg1, ok := args[1].(*Int)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return nil, ErrInvalidArgumentType{
 | 
				
			||||||
 | 
									Name:     "second",
 | 
				
			||||||
 | 
									Expected: "int",
 | 
				
			||||||
 | 
									Found:    args[1].TypeName(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							startIdx = int(arg1.Value)
 | 
				
			||||||
 | 
							if startIdx < 0 || startIdx > arrayLen {
 | 
				
			||||||
 | 
								return nil, ErrIndexOutOfBounds
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delCount := len(array.Value)
 | 
				
			||||||
 | 
						if argsLen > 2 {
 | 
				
			||||||
 | 
							arg2, ok := args[2].(*Int)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return nil, ErrInvalidArgumentType{
 | 
				
			||||||
 | 
									Name:     "third",
 | 
				
			||||||
 | 
									Expected: "int",
 | 
				
			||||||
 | 
									Found:    args[2].TypeName(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							delCount = int(arg2.Value)
 | 
				
			||||||
 | 
							if delCount < 0 {
 | 
				
			||||||
 | 
								return nil, ErrIndexOutOfBounds
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// if count of to be deleted items is bigger than expected, truncate it
 | 
				
			||||||
 | 
						if startIdx+delCount > arrayLen {
 | 
				
			||||||
 | 
							delCount = arrayLen - startIdx
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// delete items
 | 
				
			||||||
 | 
						endIdx := startIdx + delCount
 | 
				
			||||||
 | 
						deleted := append([]Object{}, array.Value[startIdx:endIdx]...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						head := array.Value[:startIdx]
 | 
				
			||||||
 | 
						var items []Object
 | 
				
			||||||
 | 
						if argsLen > 3 {
 | 
				
			||||||
 | 
							items = make([]Object, 0, argsLen-3)
 | 
				
			||||||
 | 
							for i := 3; i < argsLen; i++ {
 | 
				
			||||||
 | 
								items = append(items, args[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						items = append(items, array.Value[endIdx:]...)
 | 
				
			||||||
 | 
						array.Value = append(head, items...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// return deleted items
 | 
				
			||||||
 | 
						return &Array{Value: deleted}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								vendor/github.com/d5/tengo/v2/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/d5/tengo/v2/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -97,6 +97,7 @@ func (b *Bytecode) RemoveDuplicates() {
 | 
				
			|||||||
	var deduped []Object
 | 
						var deduped []Object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	indexMap := make(map[int]int) // mapping from old constant index to new index
 | 
						indexMap := make(map[int]int) // mapping from old constant index to new index
 | 
				
			||||||
 | 
						fns := make(map[*CompiledFunction]int)
 | 
				
			||||||
	ints := make(map[int64]int)
 | 
						ints := make(map[int64]int)
 | 
				
			||||||
	strings := make(map[string]int)
 | 
						strings := make(map[string]int)
 | 
				
			||||||
	floats := make(map[float64]int)
 | 
						floats := make(map[float64]int)
 | 
				
			||||||
@@ -106,9 +107,14 @@ func (b *Bytecode) RemoveDuplicates() {
 | 
				
			|||||||
	for curIdx, c := range b.Constants {
 | 
						for curIdx, c := range b.Constants {
 | 
				
			||||||
		switch c := c.(type) {
 | 
							switch c := c.(type) {
 | 
				
			||||||
		case *CompiledFunction:
 | 
							case *CompiledFunction:
 | 
				
			||||||
			// add to deduped list
 | 
								if newIdx, ok := fns[c]; ok {
 | 
				
			||||||
			indexMap[curIdx] = len(deduped)
 | 
									indexMap[curIdx] = newIdx
 | 
				
			||||||
			deduped = append(deduped, c)
 | 
								} else {
 | 
				
			||||||
 | 
									newIdx = len(deduped)
 | 
				
			||||||
 | 
									fns[c] = newIdx
 | 
				
			||||||
 | 
									indexMap[curIdx] = newIdx
 | 
				
			||||||
 | 
									deduped = append(deduped, c)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		case *ImmutableMap:
 | 
							case *ImmutableMap:
 | 
				
			||||||
			modName := inferModuleName(c)
 | 
								modName := inferModuleName(c)
 | 
				
			||||||
			newIdx, ok := immutableMaps[modName]
 | 
								newIdx, ok := immutableMaps[modName]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										52
									
								
								vendor/github.com/d5/tengo/v2/compiler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/d5/tengo/v2/compiler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -44,6 +44,7 @@ type Compiler struct {
 | 
				
			|||||||
	file            *parser.SourceFile
 | 
						file            *parser.SourceFile
 | 
				
			||||||
	parent          *Compiler
 | 
						parent          *Compiler
 | 
				
			||||||
	modulePath      string
 | 
						modulePath      string
 | 
				
			||||||
 | 
						importDir       string
 | 
				
			||||||
	constants       []Object
 | 
						constants       []Object
 | 
				
			||||||
	symbolTable     *SymbolTable
 | 
						symbolTable     *SymbolTable
 | 
				
			||||||
	scopes          []compilationScope
 | 
						scopes          []compilationScope
 | 
				
			||||||
@@ -505,7 +506,11 @@ func (c *Compiler) Compile(node parser.Node) error {
 | 
				
			|||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		c.emit(node, parser.OpCall, len(node.Args))
 | 
							ellipsis := 0
 | 
				
			||||||
 | 
							if node.Ellipsis.IsValid() {
 | 
				
			||||||
 | 
								ellipsis = 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							c.emit(node, parser.OpCall, len(node.Args), ellipsis)
 | 
				
			||||||
	case *parser.ImportExpr:
 | 
						case *parser.ImportExpr:
 | 
				
			||||||
		if node.ModuleName == "" {
 | 
							if node.ModuleName == "" {
 | 
				
			||||||
			return c.errorf(node, "empty module name")
 | 
								return c.errorf(node, "empty module name")
 | 
				
			||||||
@@ -520,12 +525,12 @@ func (c *Compiler) Compile(node parser.Node) error {
 | 
				
			|||||||
			switch v := v.(type) {
 | 
								switch v := v.(type) {
 | 
				
			||||||
			case []byte: // module written in Tengo
 | 
								case []byte: // module written in Tengo
 | 
				
			||||||
				compiled, err := c.compileModule(node,
 | 
									compiled, err := c.compileModule(node,
 | 
				
			||||||
					node.ModuleName, node.ModuleName, v)
 | 
										node.ModuleName, v, false)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				c.emit(node, parser.OpConstant, c.addConstant(compiled))
 | 
									c.emit(node, parser.OpConstant, c.addConstant(compiled))
 | 
				
			||||||
				c.emit(node, parser.OpCall, 0)
 | 
									c.emit(node, parser.OpCall, 0, 0)
 | 
				
			||||||
			case Object: // builtin module
 | 
								case Object: // builtin module
 | 
				
			||||||
				c.emit(node, parser.OpConstant, c.addConstant(v))
 | 
									c.emit(node, parser.OpConstant, c.addConstant(v))
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
@@ -537,29 +542,25 @@ func (c *Compiler) Compile(node parser.Node) error {
 | 
				
			|||||||
				moduleName += ".tengo"
 | 
									moduleName += ".tengo"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			modulePath, err := filepath.Abs(moduleName)
 | 
								modulePath, err := filepath.Abs(
 | 
				
			||||||
 | 
									filepath.Join(c.importDir, moduleName))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return c.errorf(node, "module file path error: %s",
 | 
									return c.errorf(node, "module file path error: %s",
 | 
				
			||||||
					err.Error())
 | 
										err.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := c.checkCyclicImports(node, modulePath); err != nil {
 | 
								moduleSrc, err := ioutil.ReadFile(modulePath)
 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			moduleSrc, err := ioutil.ReadFile(moduleName)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return c.errorf(node, "module file read error: %s",
 | 
									return c.errorf(node, "module file read error: %s",
 | 
				
			||||||
					err.Error())
 | 
										err.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			compiled, err := c.compileModule(node,
 | 
								compiled, err := c.compileModule(node, modulePath, moduleSrc, true)
 | 
				
			||||||
				moduleName, modulePath, moduleSrc)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			c.emit(node, parser.OpConstant, c.addConstant(compiled))
 | 
								c.emit(node, parser.OpConstant, c.addConstant(compiled))
 | 
				
			||||||
			c.emit(node, parser.OpCall, 0)
 | 
								c.emit(node, parser.OpCall, 0, 0)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return c.errorf(node, "module '%s' not found", node.ModuleName)
 | 
								return c.errorf(node, "module '%s' not found", node.ModuleName)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -634,6 +635,11 @@ func (c *Compiler) EnableFileImport(enable bool) {
 | 
				
			|||||||
	c.allowFileImport = enable
 | 
						c.allowFileImport = enable
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetImportDir sets the initial import directory path for file imports.
 | 
				
			||||||
 | 
					func (c *Compiler) SetImportDir(dir string) {
 | 
				
			||||||
 | 
						c.importDir = dir
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Compiler) compileAssign(
 | 
					func (c *Compiler) compileAssign(
 | 
				
			||||||
	node parser.Node,
 | 
						node parser.Node,
 | 
				
			||||||
	lhs, rhs []parser.Expr,
 | 
						lhs, rhs []parser.Expr,
 | 
				
			||||||
@@ -847,8 +853,8 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
 | 
				
			|||||||
	//     ... body ...
 | 
						//     ... body ...
 | 
				
			||||||
	//   }
 | 
						//   }
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// ":it" is a local variable but will be conflict with other user variables
 | 
						// ":it" is a local variable but it will not conflict with other user variables
 | 
				
			||||||
	// because character ":" is not allowed.
 | 
						// because character ":" is not allowed in the variable names.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// init
 | 
						// init
 | 
				
			||||||
	//   :it = iterator(iterable)
 | 
						//   :it = iterator(iterable)
 | 
				
			||||||
@@ -893,6 +899,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
 | 
				
			|||||||
		if keySymbol.Scope == ScopeGlobal {
 | 
							if keySymbol.Scope == ScopeGlobal {
 | 
				
			||||||
			c.emit(stmt, parser.OpSetGlobal, keySymbol.Index)
 | 
								c.emit(stmt, parser.OpSetGlobal, keySymbol.Index)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								keySymbol.LocalAssigned = true
 | 
				
			||||||
			c.emit(stmt, parser.OpDefineLocal, keySymbol.Index)
 | 
								c.emit(stmt, parser.OpDefineLocal, keySymbol.Index)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -909,6 +916,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
 | 
				
			|||||||
		if valueSymbol.Scope == ScopeGlobal {
 | 
							if valueSymbol.Scope == ScopeGlobal {
 | 
				
			||||||
			c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index)
 | 
								c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								valueSymbol.LocalAssigned = true
 | 
				
			||||||
			c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index)
 | 
								c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -955,8 +963,9 @@ func (c *Compiler) checkCyclicImports(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (c *Compiler) compileModule(
 | 
					func (c *Compiler) compileModule(
 | 
				
			||||||
	node parser.Node,
 | 
						node parser.Node,
 | 
				
			||||||
	moduleName, modulePath string,
 | 
						modulePath string,
 | 
				
			||||||
	src []byte,
 | 
						src []byte,
 | 
				
			||||||
 | 
						isFile bool,
 | 
				
			||||||
) (*CompiledFunction, error) {
 | 
					) (*CompiledFunction, error) {
 | 
				
			||||||
	if err := c.checkCyclicImports(node, modulePath); err != nil {
 | 
						if err := c.checkCyclicImports(node, modulePath); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -967,7 +976,7 @@ func (c *Compiler) compileModule(
 | 
				
			|||||||
		return compiledModule, nil
 | 
							return compiledModule, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modFile := c.file.Set().AddFile(moduleName, -1, len(src))
 | 
						modFile := c.file.Set().AddFile(modulePath, -1, len(src))
 | 
				
			||||||
	p := parser.NewParser(modFile, src, nil)
 | 
						p := parser.NewParser(modFile, src, nil)
 | 
				
			||||||
	file, err := p.ParseFile()
 | 
						file, err := p.ParseFile()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -984,7 +993,7 @@ func (c *Compiler) compileModule(
 | 
				
			|||||||
	symbolTable = symbolTable.Fork(false)
 | 
						symbolTable = symbolTable.Fork(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// compile module
 | 
						// compile module
 | 
				
			||||||
	moduleCompiler := c.fork(modFile, modulePath, symbolTable)
 | 
						moduleCompiler := c.fork(modFile, modulePath, symbolTable, isFile)
 | 
				
			||||||
	if err := moduleCompiler.Compile(file); err != nil {
 | 
						if err := moduleCompiler.Compile(file); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1082,10 +1091,16 @@ func (c *Compiler) fork(
 | 
				
			|||||||
	file *parser.SourceFile,
 | 
						file *parser.SourceFile,
 | 
				
			||||||
	modulePath string,
 | 
						modulePath string,
 | 
				
			||||||
	symbolTable *SymbolTable,
 | 
						symbolTable *SymbolTable,
 | 
				
			||||||
 | 
						isFile bool,
 | 
				
			||||||
) *Compiler {
 | 
					) *Compiler {
 | 
				
			||||||
	child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
 | 
						child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
 | 
				
			||||||
	child.modulePath = modulePath // module file path
 | 
						child.modulePath = modulePath // module file path
 | 
				
			||||||
	child.parent = c              // parent to set to current compiler
 | 
						child.parent = c              // parent to set to current compiler
 | 
				
			||||||
 | 
						child.allowFileImport = c.allowFileImport
 | 
				
			||||||
 | 
						child.importDir = c.importDir
 | 
				
			||||||
 | 
						if isFile && c.importDir != "" {
 | 
				
			||||||
 | 
							child.importDir = filepath.Dir(modulePath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return child
 | 
						return child
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1192,6 +1207,7 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
 | 
				
			|||||||
	var lastOp parser.Opcode
 | 
						var lastOp parser.Opcode
 | 
				
			||||||
	var appendReturn bool
 | 
						var appendReturn bool
 | 
				
			||||||
	endPos := len(c.scopes[c.scopeIndex].Instructions)
 | 
						endPos := len(c.scopes[c.scopeIndex].Instructions)
 | 
				
			||||||
 | 
						newEndPost := len(newInsts)
 | 
				
			||||||
	iterateInstructions(newInsts,
 | 
						iterateInstructions(newInsts,
 | 
				
			||||||
		func(pos int, opcode parser.Opcode, operands []int) bool {
 | 
							func(pos int, opcode parser.Opcode, operands []int) bool {
 | 
				
			||||||
			switch opcode {
 | 
								switch opcode {
 | 
				
			||||||
@@ -1204,6 +1220,8 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
 | 
				
			|||||||
				} else if endPos == operands[0] {
 | 
									} else if endPos == operands[0] {
 | 
				
			||||||
					// there's a jump instruction that jumps to the end of
 | 
										// there's a jump instruction that jumps to the end of
 | 
				
			||||||
					// function compiler should append "return".
 | 
										// function compiler should append "return".
 | 
				
			||||||
 | 
										copy(newInsts[pos:],
 | 
				
			||||||
 | 
											MakeInstruction(opcode, newEndPost))
 | 
				
			||||||
					appendReturn = true
 | 
										appendReturn = true
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					panic(fmt.Errorf("invalid jump position: %d", newDst))
 | 
										panic(fmt.Errorf("invalid jump position: %d", newDst))
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user