forked from lug/matterbridge
		
	Compare commits
	
		
			96 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 | 
							
								
								
									
										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
 | 
			
		||||
  # from this option's value:
 | 
			
		||||
  #   	vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
 | 
			
		||||
  skip-dirs:
 | 
			
		||||
  skip-dirs: gateway/bridgemap$
 | 
			
		||||
 | 
			
		||||
  # which files to skip: they will be analyzed, but issues from them
 | 
			
		||||
  # won't be reported. Default value is empty list, but there is
 | 
			
		||||
@@ -176,7 +176,13 @@ linters:
 | 
			
		||||
    - prealloc
 | 
			
		||||
    - wsl
 | 
			
		||||
    - gomnd
 | 
			
		||||
 | 
			
		||||
    - godox
 | 
			
		||||
    - goerr113
 | 
			
		||||
    - testpackage
 | 
			
		||||
    - godot
 | 
			
		||||
    - interfacer
 | 
			
		||||
    - goheader
 | 
			
		||||
    - noctx
 | 
			
		||||
 | 
			
		||||
# rules to deal with reported isues
 | 
			
		||||
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.14.x
 | 
			
		||||
    env:
 | 
			
		||||
    - GO111MODULE=on
 | 
			
		||||
    - GOLANGCI_VERSION="v1.23.7"
 | 
			
		||||
  - stage: test
 | 
			
		||||
    # Run tests in a combination of Go environments.
 | 
			
		||||
    script: ./ci/test.sh
 | 
			
		||||
    go: 1.13.x
 | 
			
		||||
    env:
 | 
			
		||||
    - GOFLAGS=-mod=vendor
 | 
			
		||||
  - script: ./ci/test.sh
 | 
			
		||||
    go: 1.13.x
 | 
			
		||||
    env:
 | 
			
		||||
    - GO111MODULE=on
 | 
			
		||||
  - script: ./ci/test.sh
 | 
			
		||||
    go: 1.14.x
 | 
			
		||||
    env:
 | 
			
		||||
    - GO111MODULE=on
 | 
			
		||||
    - REPORT_COVERAGE=1
 | 
			
		||||
    - BINDEPLOY=1
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
ENTRYPOINT ["/bin/matterbridge"]
 | 
			
		||||
FROM alpine:edge AS builder
 | 
			
		||||
 | 
			
		||||
COPY . /go/src/github.com/42wim/matterbridge
 | 
			
		||||
RUN apk update && apk add go git gcc musl-dev ca-certificates mailcap \
 | 
			
		||||
RUN apk update && apk add go git gcc musl-dev \
 | 
			
		||||
        && cd /go/src/github.com/42wim/matterbridge \
 | 
			
		||||
        && export GOPATH=/go \
 | 
			
		||||
        && go get \
 | 
			
		||||
        && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
 | 
			
		||||
        && rm -rf /go \
 | 
			
		||||
        && apk del --purge git go gcc musl-dev
 | 
			
		||||
        && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
 | 
			
		||||
 | 
			
		||||
FROM alpine:edge
 | 
			
		||||
RUN apk --no-cache add ca-certificates mailcap
 | 
			
		||||
COPY --from=builder /bin/matterbridge /bin/matterbridge
 | 
			
		||||
RUN mkdir /etc/matterbridge \
 | 
			
		||||
  && touch /etc/matterbridge/matterbridge.toml \
 | 
			
		||||
  && ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml
 | 
			
		||||
ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								README.md
									
									
									
									
									
								
							@@ -9,26 +9,26 @@ Letting people be where they want to be.<br />
 | 
			
		||||
 | 
			
		||||
   <sup>
 | 
			
		||||
 | 
			
		||||
[Discord][mb-discord] |
 | 
			
		||||
[Gitter][mb-gitter] |
 | 
			
		||||
[IRC][mb-irc] |
 | 
			
		||||
[Discord][mb-discord] |
 | 
			
		||||
[Keybase][mb-keybase] |
 | 
			
		||||
[Matrix][mb-matrix] |
 | 
			
		||||
[Slack][mb-slack] |
 | 
			
		||||
[Mattermost][mb-mattermost] |
 | 
			
		||||
[MSTeams][mb-msteams] |
 | 
			
		||||
[Rocket.Chat][mb-rocketchat] |
 | 
			
		||||
[XMPP][mb-xmpp] |
 | 
			
		||||
[Slack][mb-slack] |
 | 
			
		||||
[Telegram][mb-telegram] |
 | 
			
		||||
[Twitch][mb-twitch] |
 | 
			
		||||
[WhatsApp][mb-whatsapp] |
 | 
			
		||||
[XMPP][mb-xmpp] |
 | 
			
		||||
[Zulip][mb-zulip] |
 | 
			
		||||
[Telegram][mb-telegram] |
 | 
			
		||||
[Keybase][mb-keybase] |
 | 
			
		||||
And more...
 | 
			
		||||
</sup>
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
[](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/test_coverage)<br />
 | 
			
		||||
 | 
			
		||||
@@ -86,29 +86,32 @@ And more...
 | 
			
		||||
 | 
			
		||||
### Natively supported
 | 
			
		||||
 | 
			
		||||
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
 | 
			
		||||
- [IRC](http://www.mirc.com/servers.html)
 | 
			
		||||
- [XMPP](https://xmpp.org)
 | 
			
		||||
- [Gitter](https://gitter.im)
 | 
			
		||||
- [Slack](https://slack.com)
 | 
			
		||||
- [Discord](https://discordapp.com)
 | 
			
		||||
- [Telegram](https://telegram.org)
 | 
			
		||||
- [Rocket.chat](https://rocket.chat)
 | 
			
		||||
- [Matrix](https://matrix.org)
 | 
			
		||||
- [Steam](https://store.steampowered.com/)
 | 
			
		||||
- [Twitch](https://twitch.tv)
 | 
			
		||||
- [Ssh-chat](https://github.com/shazow/ssh-chat)
 | 
			
		||||
- [WhatsApp](https://www.whatsapp.com/)
 | 
			
		||||
- [Zulip](https://zulipchat.com)
 | 
			
		||||
- [Gitter](https://gitter.im)
 | 
			
		||||
- [IRC](http://www.mirc.com/servers.html)
 | 
			
		||||
- [Keybase](https://keybase.io)
 | 
			
		||||
- [Matrix](https://matrix.org)
 | 
			
		||||
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
 | 
			
		||||
- [Microsoft Teams](https://teams.microsoft.com)
 | 
			
		||||
- [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
 | 
			
		||||
 | 
			
		||||
- [Discourse](https://github.com/DeclanHoare/matterbabble)
 | 
			
		||||
- [Facebook messenger](https://github.com/VictorNine/fbridge)
 | 
			
		||||
- [Minecraft](https://github.com/elytra/MatterLink)
 | 
			
		||||
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
 | 
			
		||||
- [Facebook messenger](https://github.com/VictorNine/fbridge)
 | 
			
		||||
- [Discourse](https://github.com/DeclanHoare/matterbabble)
 | 
			
		||||
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
 | 
			
		||||
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
 | 
			
		||||
 | 
			
		||||
### API
 | 
			
		||||
 | 
			
		||||
@@ -128,18 +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:
 | 
			
		||||
 | 
			
		||||
- [Discord][mb-discord]
 | 
			
		||||
- [Gitter][mb-gitter]
 | 
			
		||||
- [IRC][mb-irc]
 | 
			
		||||
- [Discord][mb-discord]
 | 
			
		||||
- [Keybase][mb-keybase]
 | 
			
		||||
- [Matrix][mb-matrix]
 | 
			
		||||
- [Slack][mb-slack]
 | 
			
		||||
- [Mattermost][mb-mattermost]
 | 
			
		||||
- [Rocket.Chat][mb-rocketchat]
 | 
			
		||||
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
 | 
			
		||||
- [Twitch][mb-twitch]
 | 
			
		||||
- [Zulip][mb-zulip]
 | 
			
		||||
- [Slack][mb-slack]
 | 
			
		||||
- [Telegram][mb-telegram]
 | 
			
		||||
- [Keybase][mb-keybase]
 | 
			
		||||
- [Twitch][mb-twitch]
 | 
			
		||||
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
 | 
			
		||||
- [Zulip][mb-zulip]
 | 
			
		||||
 | 
			
		||||
## Screenshots
 | 
			
		||||
 | 
			
		||||
@@ -149,14 +152,15 @@ See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
### Binaries
 | 
			
		||||
 | 
			
		||||
- Latest stable release [v1.17.0](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
 | 
			
		||||
- Latest stable release [v1.18.1](https://github.com/42wim/matterbridge/releases/latest)
 | 
			
		||||
- 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.
 | 
			
		||||
 | 
			
		||||
### Packages
 | 
			
		||||
 | 
			
		||||
- [Overview](https://repology.org/metapackage/matterbridge/versions)
 | 
			
		||||
- [snap](https://snapcraft.io/matterbridge)
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 | 
			
		||||
@@ -304,6 +308,7 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
 | 
			
		||||
- https://kopano.com/blog/matterbridge-bridging-mattermost-chat/
 | 
			
		||||
- https://www.stitcher.com/s/?eid=52382713
 | 
			
		||||
- https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
 | 
			
		||||
- https://userlinux.net/mattermost-and-matterbridge.html
 | 
			
		||||
 | 
			
		||||
## Thanks
 | 
			
		||||
 | 
			
		||||
@@ -322,30 +327,32 @@ Matterbridge wouldn't exist without these libraries:
 | 
			
		||||
- gops - https://github.com/google/gops
 | 
			
		||||
- gozulipbot - https://github.com/ifo/gozulipbot
 | 
			
		||||
- irc - https://github.com/lrstanley/girc
 | 
			
		||||
- mattermost - https://github.com/mattermost/mattermost-server
 | 
			
		||||
- keybase - https://github.com/keybase/go-keybase-chat-bot
 | 
			
		||||
- matrix - https://github.com/matrix-org/gomatrix
 | 
			
		||||
- sshchat - https://github.com/shazow/ssh-chat
 | 
			
		||||
- mattermost - https://github.com/mattermost/mattermost-server
 | 
			
		||||
- msgraph.go - https://github.com/yaegashi/msgraph.go
 | 
			
		||||
- slack - https://github.com/nlopes/slack
 | 
			
		||||
- sshchat - https://github.com/shazow/ssh-chat
 | 
			
		||||
- steam - https://github.com/Philipp15b/go-steam
 | 
			
		||||
- telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
 | 
			
		||||
- xmpp - https://github.com/mattn/go-xmpp
 | 
			
		||||
- whatsapp - https://github.com/Rhymen/go-whatsapp/
 | 
			
		||||
- zulip - https://github.com/ifo/gozulipbot
 | 
			
		||||
- tengo - https://github.com/d5/tengo
 | 
			
		||||
- keybase - https://github.com/keybase/go-keybase-chat-bot
 | 
			
		||||
- whatsapp - https://github.com/Rhymen/go-whatsapp/
 | 
			
		||||
- xmpp - https://github.com/mattn/go-xmpp
 | 
			
		||||
- zulip - https://github.com/ifo/gozulipbot
 | 
			
		||||
 | 
			
		||||
<!-- Links -->
 | 
			
		||||
 | 
			
		||||
[mb-discord]: https://discord.gg/AkKPtrQ
 | 
			
		||||
[mb-gitter]: https://gitter.im/42wim/matterbridge
 | 
			
		||||
[mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat
 | 
			
		||||
[mb-discord]: https://discord.gg/AkKPtrQ
 | 
			
		||||
[mb-keybase]: https://keybase.io/team/matterbridge
 | 
			
		||||
[mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org
 | 
			
		||||
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
 | 
			
		||||
[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
 | 
			
		||||
[mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7
 | 
			
		||||
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
 | 
			
		||||
[mb-xmpp]: https://inverse.chat/
 | 
			
		||||
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
 | 
			
		||||
[mb-telegram]: https://t.me/Matterbridge
 | 
			
		||||
[mb-twitch]: https://www.twitch.tv/matterbridge
 | 
			
		||||
[mb-whatsapp]: https://www.whatsapp.com/
 | 
			
		||||
[mb-keybase]: https://keybase.io/team/matterbridge
 | 
			
		||||
[mb-xmpp]: https://inverse.chat/
 | 
			
		||||
[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/config"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/labstack/echo/v4"
 | 
			
		||||
	"github.com/labstack/echo/v4/middleware"
 | 
			
		||||
	"github.com/zfjagann/golang-ring"
 | 
			
		||||
	ring "github.com/zfjagann/golang-ring"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type API struct {
 | 
			
		||||
@@ -41,9 +42,17 @@ func New(cfg *bridge.Config) bridge.Bridger {
 | 
			
		||||
			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/messages", b.handleMessages)
 | 
			
		||||
	e.GET("/api/stream", b.handleStream)
 | 
			
		||||
	e.GET("/api/websocket", b.handleWebsocket)
 | 
			
		||||
	e.POST("/api/message", b.handlePostMessage)
 | 
			
		||||
	go func() {
 | 
			
		||||
		if b.GetString("BindAddress") == "" {
 | 
			
		||||
@@ -106,13 +115,17 @@ func (b *API) handleMessages(c echo.Context) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *API) handleStream(c echo.Context) error {
 | 
			
		||||
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
 | 
			
		||||
	c.Response().WriteHeader(http.StatusOK)
 | 
			
		||||
	greet := config.Message{
 | 
			
		||||
func (b *API) getGreeting() config.Message {
 | 
			
		||||
	return config.Message{
 | 
			
		||||
		Event:     config.EventAPIConnected,
 | 
			
		||||
		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 {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -128,3 +141,52 @@ func (b *API) handleStream(c echo.Context) error {
 | 
			
		||||
		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"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
@@ -74,6 +75,7 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
 | 
			
		||||
	for ID, channel := range channels {
 | 
			
		||||
		if !exists[ID] {
 | 
			
		||||
			b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
 | 
			
		||||
			time.Sleep(time.Duration(b.GetInt("JoinDelay")) * time.Millisecond)
 | 
			
		||||
			err := b.JoinChannel(channel)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
@@ -84,8 +86,16 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
 | 
			
		||||
	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 {
 | 
			
		||||
	val, ok := b.Config.GetBool(b.Account + "." + key)
 | 
			
		||||
	val, ok := b.Config.GetBool(b.GetConfigKey(key))
 | 
			
		||||
	if !ok {
 | 
			
		||||
		val, _ = b.Config.GetBool("general." + key)
 | 
			
		||||
	}
 | 
			
		||||
@@ -93,7 +103,7 @@ func (b *Bridge) GetBool(key string) bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
		val, _ = b.Config.GetInt("general." + key)
 | 
			
		||||
	}
 | 
			
		||||
@@ -101,7 +111,7 @@ func (b *Bridge) GetInt(key string) int {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
		val, _ = b.Config.GetString("general." + key)
 | 
			
		||||
	}
 | 
			
		||||
@@ -109,7 +119,7 @@ func (b *Bridge) GetString(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 {
 | 
			
		||||
		val, _ = b.Config.GetStringSlice("general." + key)
 | 
			
		||||
	}
 | 
			
		||||
@@ -117,7 +127,7 @@ func (b *Bridge) GetStringSlice(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 {
 | 
			
		||||
		val, _ = b.Config.GetStringSlice2D("general." + key)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package config
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -84,18 +85,22 @@ type Protocol struct {
 | 
			
		||||
	DisableWebPagePreview  bool   // telegram
 | 
			
		||||
	EditSuffix             string // mattermost, slack, discord, telegram, gitter
 | 
			
		||||
	EditDisable            bool   // mattermost, slack, discord, telegram, gitter
 | 
			
		||||
	HTMLDisable            bool   // matrix
 | 
			
		||||
	IconURL                string // mattermost, slack
 | 
			
		||||
	IgnoreFailureOnStart   bool   // general
 | 
			
		||||
	IgnoreNicks            string // all protocols
 | 
			
		||||
	IgnoreMessages         string // all protocols
 | 
			
		||||
	Jid                    string // xmpp
 | 
			
		||||
	JoinDelay              string // all protocols
 | 
			
		||||
	Label                  string // all protocols
 | 
			
		||||
	Login                  string // mattermost, matrix
 | 
			
		||||
	LogFile                string // general
 | 
			
		||||
	MediaDownloadBlackList []string
 | 
			
		||||
	MediaDownloadPath      string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
 | 
			
		||||
	MediaDownloadSize      int    // all protocols
 | 
			
		||||
	MediaServerDownload    string
 | 
			
		||||
	MediaServerUpload      string
 | 
			
		||||
	MediaConvertTgs        string     // telegram
 | 
			
		||||
	MediaConvertWebPToPNG  bool       // telegram
 | 
			
		||||
	MessageDelay           int        // IRC, time in millisecond to wait between messages
 | 
			
		||||
	MessageFormat          string     // telegram
 | 
			
		||||
@@ -134,6 +139,7 @@ type Protocol struct {
 | 
			
		||||
	SkipTLSVerify          bool       // IRC, mattermost
 | 
			
		||||
	SkipVersionCheck       bool       // mattermost
 | 
			
		||||
	StripNick              bool       // all protocols
 | 
			
		||||
	StripMarkdown          bool       // irc
 | 
			
		||||
	SyncTopic              bool       // slack
 | 
			
		||||
	TengoModifyMessage     string     // general
 | 
			
		||||
	Team                   string     // mattermost, keybase
 | 
			
		||||
@@ -216,6 +222,7 @@ type BridgeValues struct {
 | 
			
		||||
type Config interface {
 | 
			
		||||
	Viper() *viper.Viper
 | 
			
		||||
	BridgeValues() *BridgeValues
 | 
			
		||||
	IsKeySet(key string) bool
 | 
			
		||||
	GetBool(key string) (bool, bool)
 | 
			
		||||
	GetInt(key string) (int, bool)
 | 
			
		||||
	GetString(key string) (string, bool)
 | 
			
		||||
@@ -243,6 +250,15 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
		mycfg.cv.General.MediaDownloadSize = 1000000
 | 
			
		||||
	}
 | 
			
		||||
@@ -300,6 +316,12 @@ func (c *config) Viper() *viper.Viper {
 | 
			
		||||
	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) {
 | 
			
		||||
	c.RLock()
 | 
			
		||||
	defer c.RUnlock()
 | 
			
		||||
@@ -359,6 +381,11 @@ type TestConfig struct {
 | 
			
		||||
	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) {
 | 
			
		||||
	val, ok := c.Overrides[key]
 | 
			
		||||
	if ok {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ type Bdiscord struct {
 | 
			
		||||
	c *discordgo.Session
 | 
			
		||||
 | 
			
		||||
	nick            string
 | 
			
		||||
	userID          string
 | 
			
		||||
	guildID         string
 | 
			
		||||
	webhookID       string
 | 
			
		||||
	webhookToken    string
 | 
			
		||||
@@ -33,6 +34,8 @@ type Bdiscord struct {
 | 
			
		||||
	membersMutex  sync.RWMutex
 | 
			
		||||
	userMemberMap map[string]*discordgo.Member
 | 
			
		||||
	nickMemberMap map[string]*discordgo.Member
 | 
			
		||||
	webhookCache  map[string]string
 | 
			
		||||
	webhookMutex  sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
			
		||||
@@ -40,6 +43,7 @@ func New(cfg *bridge.Config) bridge.Bridger {
 | 
			
		||||
	b.userMemberMap = make(map[string]*discordgo.Member)
 | 
			
		||||
	b.nickMemberMap = make(map[string]*discordgo.Member)
 | 
			
		||||
	b.channelInfoMap = make(map[string]*config.ChannelInfo)
 | 
			
		||||
	b.webhookCache = make(map[string]string)
 | 
			
		||||
	if b.GetString("WebhookURL") != "" {
 | 
			
		||||
		b.Log.Debug("Configuring Discord Incoming Webhook")
 | 
			
		||||
		b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
 | 
			
		||||
@@ -92,6 +96,7 @@ func (b *Bdiscord) Connect() error {
 | 
			
		||||
	}
 | 
			
		||||
	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1)
 | 
			
		||||
	b.nick = userinfo.Username
 | 
			
		||||
	b.userID = userinfo.ID
 | 
			
		||||
	b.channelsMutex.Lock()
 | 
			
		||||
	for _, guild := range guilds {
 | 
			
		||||
		if guild.Name == serverName || guild.ID == serverName {
 | 
			
		||||
@@ -186,6 +191,8 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
			
		||||
	b.Log.Debugf("=> Receiving %#v", msg)
 | 
			
		||||
 | 
			
		||||
	origMsgID := msg.ID
 | 
			
		||||
 | 
			
		||||
	channelID := b.getChannelID(msg.Channel)
 | 
			
		||||
	if channelID == "" {
 | 
			
		||||
		return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
 | 
			
		||||
@@ -222,12 +229,13 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
			
		||||
	// Use webhook to send the message
 | 
			
		||||
	if wID != "" && msg.Event != config.EventMsgDelete {
 | 
			
		||||
		// skip events
 | 
			
		||||
		if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
 | 
			
		||||
		if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we are editing a message, delete the old message
 | 
			
		||||
		if msg.ID != "" {
 | 
			
		||||
			msg.ID = b.getCacheID(msg.ID)
 | 
			
		||||
			b.Log.Debugf("Deleting edited webhook message")
 | 
			
		||||
			err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -271,6 +279,8 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
			
		||||
		if msg == nil {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		b.updateCacheID(origMsgID, msg.ID)
 | 
			
		||||
		return msg.ID, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -281,6 +291,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
 | 
			
		||||
		if msg.ID == "" {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
		msg.ID = b.getCacheID(msg.ID)
 | 
			
		||||
		err := b.c.ChannelMessageDelete(channelID, msg.ID)
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,11 @@ func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ignore our own typing messages
 | 
			
		||||
	if m.UserID == b.userID {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
 | 
			
		||||
	rmsg.Channel = b.getChannelName(m.ChannelID)
 | 
			
		||||
	b.Remote <- rmsg
 | 
			
		||||
@@ -119,6 +124,9 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
 | 
			
		||||
		rmsg.Event = config.EventUserAction
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Replace emotes
 | 
			
		||||
	rmsg.Text = replaceEmotes(rmsg.Text)
 | 
			
		||||
 | 
			
		||||
	b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account)
 | 
			
		||||
	b.Log.Debugf("<= Message is %#v", rmsg)
 | 
			
		||||
	b.Remote <- rmsg
 | 
			
		||||
@@ -209,7 +217,7 @@ func handleEmbed(embed *discordgo.MessageEmbed) string {
 | 
			
		||||
 | 
			
		||||
		i++
 | 
			
		||||
		if i == 1 {
 | 
			
		||||
			result += "embed: " + e
 | 
			
		||||
			result += " embed: " + e
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,14 +20,14 @@ func TestHandleEmbed(t *testing.T) {
 | 
			
		||||
			embed: &discordgo.MessageEmbed{
 | 
			
		||||
				Title: "blah",
 | 
			
		||||
			},
 | 
			
		||||
			result: "embed: blah\n",
 | 
			
		||||
			result: " embed: blah\n",
 | 
			
		||||
		},
 | 
			
		||||
		"two": {
 | 
			
		||||
			embed: &discordgo.MessageEmbed{
 | 
			
		||||
				Title:       "blah",
 | 
			
		||||
				Description: "blah2",
 | 
			
		||||
			},
 | 
			
		||||
			result: "embed: blah - blah2\n",
 | 
			
		||||
			result: " embed: blah - blah2\n",
 | 
			
		||||
		},
 | 
			
		||||
		"three": {
 | 
			
		||||
			embed: &discordgo.MessageEmbed{
 | 
			
		||||
@@ -35,20 +35,20 @@ func TestHandleEmbed(t *testing.T) {
 | 
			
		||||
				Description: "blah2",
 | 
			
		||||
				URL:         "blah3",
 | 
			
		||||
			},
 | 
			
		||||
			result: "embed: blah - blah2 - blah3\n",
 | 
			
		||||
			result: " embed: blah - blah2 - blah3\n",
 | 
			
		||||
		},
 | 
			
		||||
		"twob": {
 | 
			
		||||
			embed: &discordgo.MessageEmbed{
 | 
			
		||||
				Description: "blah2",
 | 
			
		||||
				URL:         "blah3",
 | 
			
		||||
			},
 | 
			
		||||
			result: "embed: blah2 - blah3\n",
 | 
			
		||||
			result: " embed: blah2 - blah3\n",
 | 
			
		||||
		},
 | 
			
		||||
		"oneb": {
 | 
			
		||||
			embed: &discordgo.MessageEmbed{
 | 
			
		||||
				URL: "blah3",
 | 
			
		||||
			},
 | 
			
		||||
			result: "embed: blah3\n",
 | 
			
		||||
			result: " embed: blah3\n",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -137,6 +137,7 @@ var (
 | 
			
		||||
	// See https://discordapp.com/developers/docs/reference#message-formatting.
 | 
			
		||||
	channelMentionRE = regexp.MustCompile("<#[0-9]+>")
 | 
			
		||||
	userMentionRE    = regexp.MustCompile("@[^@\n]{1,32}")
 | 
			
		||||
	emoteRE          = regexp.MustCompile(`<a?(:\w+:)\d+>`)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (b *Bdiscord) replaceChannelMentions(text string) string {
 | 
			
		||||
@@ -182,9 +183,14 @@ func (b *Bdiscord) replaceUserMentions(text string) string {
 | 
			
		||||
	return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func replaceEmotes(text string) string {
 | 
			
		||||
	return emoteRE.ReplaceAllString(text, "$1")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bdiscord) replaceAction(text string) (string, bool) {
 | 
			
		||||
	if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") {
 | 
			
		||||
		return text[1 : len(text)-1], true
 | 
			
		||||
	length := len(text)
 | 
			
		||||
	if length > 1 && text[0] == '_' && text[length-1] == '_' {
 | 
			
		||||
		return text[1 : length-1], true
 | 
			
		||||
	}
 | 
			
		||||
	return text, false
 | 
			
		||||
}
 | 
			
		||||
@@ -203,6 +209,40 @@ func (b *Bdiscord) splitURL(url string) (string, string) {
 | 
			
		||||
	return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getcacheID tries to find a corresponding msgID in the webhook cache.
 | 
			
		||||
// if not found returns the original request.
 | 
			
		||||
func (b *Bdiscord) getCacheID(msgID string) string {
 | 
			
		||||
	b.webhookMutex.RLock()
 | 
			
		||||
	defer b.webhookMutex.RUnlock()
 | 
			
		||||
	for k, v := range b.webhookCache {
 | 
			
		||||
		if msgID == k {
 | 
			
		||||
			return v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return msgID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// updateCacheID updates the cache so that the newID takes the place of
 | 
			
		||||
// the original ID. This is used for edit/deletes in combination with webhooks
 | 
			
		||||
// as editing a message via webhook means deleting the message and creating a
 | 
			
		||||
// new message (with a new ID). This ID needs to be set instead of the original ID
 | 
			
		||||
func (b *Bdiscord) updateCacheID(origID, newID string) {
 | 
			
		||||
	b.webhookMutex.Lock()
 | 
			
		||||
	match := false
 | 
			
		||||
	for k, v := range b.webhookCache {
 | 
			
		||||
		if v == origID {
 | 
			
		||||
			delete(b.webhookCache, k)
 | 
			
		||||
			b.webhookCache[origID] = newID
 | 
			
		||||
			match = true
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !match && origID != "" {
 | 
			
		||||
		b.webhookCache[origID] = newID
 | 
			
		||||
	}
 | 
			
		||||
	b.webhookMutex.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func enumerateUsernames(s string) []string {
 | 
			
		||||
	onlySpace := true
 | 
			
		||||
	for _, r := range s {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,10 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"image/png"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -192,7 +195,7 @@ func ParseMarkdown(input string) string {
 | 
			
		||||
	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 {
 | 
			
		||||
	r := bytes.NewReader(*data)
 | 
			
		||||
	m, err := webp.Decode(r)
 | 
			
		||||
@@ -207,3 +210,49 @@ func ConvertWebPToPNG(data *[]byte) error {
 | 
			
		||||
	*data = w.Bytes()
 | 
			
		||||
	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/helper"
 | 
			
		||||
	"github.com/dfordsoft/golib/ic"
 | 
			
		||||
	"github.com/lrstanley/girc"
 | 
			
		||||
	"github.com/missdeer/golib/ic"
 | 
			
		||||
	"github.com/paulrosania/go-charset/charset"
 | 
			
		||||
	"github.com/saintfish/chardet"
 | 
			
		||||
 | 
			
		||||
@@ -54,12 +54,12 @@ func (b *Birc) handleFiles(msg *config.Message) bool {
 | 
			
		||||
	for _, f := range msg.Extra["file"] {
 | 
			
		||||
		fi := f.(config.FileInfo)
 | 
			
		||||
		if fi.Comment != "" {
 | 
			
		||||
			msg.Text += fi.Comment + ": "
 | 
			
		||||
			msg.Text += fi.Comment + " : "
 | 
			
		||||
		}
 | 
			
		||||
		if fi.URL != "" {
 | 
			
		||||
			msg.Text = fi.URL
 | 
			
		||||
			if fi.Comment != "" {
 | 
			
		||||
				msg.Text = fi.Comment + ": " + fi.URL
 | 
			
		||||
				msg.Text = fi.Comment + " : " + fi.URL
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	"github.com/lrstanley/girc"
 | 
			
		||||
	stripmd "github.com/writeas/go-strip-markdown"
 | 
			
		||||
 | 
			
		||||
	// We need to import the 'data' package as an implicit dependency.
 | 
			
		||||
	// See: https://godoc.org/github.com/paulrosania/go-charset/charset
 | 
			
		||||
@@ -156,6 +157,10 @@ func (b *Birc) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var msgLines []string
 | 
			
		||||
	if b.GetBool("StripMarkdown") {
 | 
			
		||||
		msg.Text = stripmd.Strip(msg.Text)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.GetBool("MessageSplit") {
 | 
			
		||||
		msgLines = helper.GetSubLines(msg.Text, b.MessageLength)
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -201,7 +206,7 @@ func (b *Birc) doSend() {
 | 
			
		||||
	for msg := range b.Local {
 | 
			
		||||
		<-throttle.C
 | 
			
		||||
		username := msg.Username
 | 
			
		||||
		if b.GetBool("Colornicks") {
 | 
			
		||||
		if b.GetBool("Colornicks") && len(username) > 1 {
 | 
			
		||||
			checksum := crc32.ChecksumIEEE([]byte(msg.Username))
 | 
			
		||||
			colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
 | 
			
		||||
			username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
 | 
			
		||||
@@ -245,6 +250,8 @@ func (b *Birc) getClient() (*girc.Client, error) {
 | 
			
		||||
		SSL:        b.GetBool("UseTLS"),
 | 
			
		||||
		TLSConfig:  &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
 | 
			
		||||
		PingDelay:  time.Minute,
 | 
			
		||||
		// skip gIRC internal rate limiting, since we have our own throttling
 | 
			
		||||
		AllowFlood: true,
 | 
			
		||||
	})
 | 
			
		||||
	return i, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,14 @@ package bmatrix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html"
 | 
			
		||||
	"mime"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
@@ -20,13 +22,21 @@ type Bmatrix struct {
 | 
			
		||||
	UserID  string
 | 
			
		||||
	RoomMap map[string]string
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	htmlTag *regexp.Regexp
 | 
			
		||||
	htmlTag            *regexp.Regexp
 | 
			
		||||
	htmlReplacementTag *regexp.Regexp
 | 
			
		||||
	*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 {
 | 
			
		||||
	b := &Bmatrix{Config: cfg}
 | 
			
		||||
	b.htmlTag = regexp.MustCompile("</.*?>")
 | 
			
		||||
	b.htmlReplacementTag = regexp.MustCompile("<[^>]*>")
 | 
			
		||||
	b.RoomMap = make(map[string]string)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
@@ -58,14 +68,25 @@ func (b *Bmatrix) Disconnect() error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
retry:
 | 
			
		||||
	resp, err := b.mc.JoinRoom(channel.Name, "", 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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.Lock()
 | 
			
		||||
	b.RoomMap[resp.RoomID] = channel.Name
 | 
			
		||||
	b.Unlock()
 | 
			
		||||
	return err
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	if b.htmlTag.MatchString(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)
 | 
			
		||||
	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 {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
@@ -372,6 +408,27 @@ func (b *Bmatrix) getAvatarURL(sender string) string {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	url := strings.ReplaceAll(mxcURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/")
 | 
			
		||||
	url += "?width=37&height=37&method=crop"
 | 
			
		||||
	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/helper"
 | 
			
		||||
	"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
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterclient"
 | 
			
		||||
	"github.com/42wim/matterbridge/matterhook"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/model"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/v5/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (b *Bmattermost) doConnectWebhookBind() error {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
 | 
			
		||||
	msgraph "github.com/matterbridge/msgraph.go/beta"
 | 
			
		||||
	msgraph "github.com/yaegashi/msgraph.go/beta"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (b *Bmsteams) findFile(weburl string) (string, error) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,11 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
 | 
			
		||||
	//	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
	msgraph "github.com/matterbridge/msgraph.go/beta"
 | 
			
		||||
	"github.com/matterbridge/msgraph.go/msauth"
 | 
			
		||||
	"github.com/mattn/godown"
 | 
			
		||||
	msgraph "github.com/yaegashi/msgraph.go/beta"
 | 
			
		||||
	"github.com/yaegashi/msgraph.go/msauth"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
)
 | 
			
		||||
@@ -72,7 +72,15 @@ func (b *Bmsteams) Disconnect() error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error {
 | 
			
		||||
	go b.poll(channel.Name)
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -121,12 +129,12 @@ func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//nolint:gocognit
 | 
			
		||||
func (b *Bmsteams) poll(channelName string) {
 | 
			
		||||
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 {
 | 
			
		||||
		panic(err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, msg := range res {
 | 
			
		||||
		msgmap[*msg.ID] = *msg.CreatedDateTime
 | 
			
		||||
@@ -139,7 +147,7 @@ func (b *Bmsteams) poll(channelName string) {
 | 
			
		||||
	for {
 | 
			
		||||
		res, err := b.getMessages(channelName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for i := len(res) - 1; i >= 0; i-- {
 | 
			
		||||
			msg := res[i]
 | 
			
		||||
@@ -151,11 +159,22 @@ func (b *Bmsteams) poll(channelName string) {
 | 
			
		||||
					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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/matterbridge/Rocket.Chat.Go.SDK/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (b *Brocketchat) handleRocket() {
 | 
			
		||||
@@ -38,6 +39,23 @@ func (b *Brocketchat) handleRocketHook(messages chan *config.Message) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Brocketchat) handleStatusEvent(ev models.Message, rmsg *config.Message) bool {
 | 
			
		||||
	switch ev.Type {
 | 
			
		||||
	case "":
 | 
			
		||||
		// this is a normal message, no processing needed
 | 
			
		||||
		// return true so the message is not dropped
 | 
			
		||||
		return true
 | 
			
		||||
	case sUserJoined, sUserLeft:
 | 
			
		||||
		rmsg.Event = config.EventJoinLeave
 | 
			
		||||
		return true
 | 
			
		||||
	case sRoomChangedTopic:
 | 
			
		||||
		rmsg.Event = config.EventTopicChange
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	b.Log.Debugf("Dropping message with unknown type: %s", ev.Type)
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
 | 
			
		||||
	for message := range b.messageChan {
 | 
			
		||||
		// skip messages with same ID, apparently messages get duplicated for an unknown reason
 | 
			
		||||
@@ -59,7 +77,12 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
 | 
			
		||||
			UserID:   message.User.ID,
 | 
			
		||||
			ID:       message.ID,
 | 
			
		||||
		}
 | 
			
		||||
		messages <- rmsg
 | 
			
		||||
 | 
			
		||||
		// handleStatusEvent returns false if the message should be dropped
 | 
			
		||||
		// in that case it is probably some modification to the channel we do not want to relay
 | 
			
		||||
		if b.handleStatusEvent(m, rmsg) {
 | 
			
		||||
			messages <- rmsg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,12 @@ type Brocketchat struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	sUserJoined       = "uj"
 | 
			
		||||
	sUserLeft         = "ul"
 | 
			
		||||
	sRoomChangedTopic = "room_changed_topic"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
			
		||||
	newCache, err := lru.New(100)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package bslack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -10,9 +11,12 @@ import (
 | 
			
		||||
	"github.com/slack-go/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrEventIgnored is for events that should be ignored
 | 
			
		||||
var ErrEventIgnored = errors.New("this event message should ignored")
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) handleSlack() {
 | 
			
		||||
	messages := make(chan *config.Message)
 | 
			
		||||
	if b.GetString(incomingWebhookConfig) != "" {
 | 
			
		||||
	if b.GetString(incomingWebhookConfig) != "" && b.GetString(tokenConfig) == "" {
 | 
			
		||||
		b.Log.Debugf("Choosing webhooks based receiving")
 | 
			
		||||
		go b.handleMatterHook(messages)
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -53,7 +57,9 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			rmsg, err := b.handleTypingEvent(ev)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			if err == ErrEventIgnored {
 | 
			
		||||
				continue
 | 
			
		||||
			} else if err != nil {
 | 
			
		||||
				b.Log.Errorf("%#v", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
@@ -131,12 +137,6 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
 | 
			
		||||
		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 ev.SubMessage != nil {
 | 
			
		||||
		// It seems ev.SubMessage.Edited == nil when slack unfurls.
 | 
			
		||||
		// Do not forward these messages. See Github issue #266.
 | 
			
		||||
@@ -149,6 +149,16 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
 | 
			
		||||
		if ev.SubType == "message_replied" && ev.Hidden {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if len(ev.SubMessage.Blocks.BlockSet) == 1 {
 | 
			
		||||
			block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock)
 | 
			
		||||
			hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Skip any messages that we made ourselves or from 'slackbot' (see #527).
 | 
			
		||||
	if ev.Username == sSlackBotUser ||
 | 
			
		||||
		(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ev.Files) > 0 {
 | 
			
		||||
@@ -276,6 +286,9 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
 | 
			
		||||
	if ev.User == b.si.User.ID {
 | 
			
		||||
		return nil, ErrEventIgnored
 | 
			
		||||
	}
 | 
			
		||||
	channelInfo, err := b.channels.getChannelByID(ev.Channel)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,7 @@ const (
 | 
			
		||||
	editSuffixConfig      = "EditSuffix"
 | 
			
		||||
	iconURLConfig         = "iconurl"
 | 
			
		||||
	noSendJoinConfig      = "nosendjoinpart"
 | 
			
		||||
	messageLength         = 3000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
			
		||||
@@ -194,6 +195,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
		b.Log.Debugf("=> Receiving %#v", msg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg.Text = helper.ClipMessage(msg.Text, messageLength)
 | 
			
		||||
	msg.Text = b.replaceCodeFence(msg.Text)
 | 
			
		||||
 | 
			
		||||
	// Make a action /me of the message
 | 
			
		||||
@@ -202,7 +204,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Use webhook to send the message
 | 
			
		||||
	if b.GetString(outgoingWebhookConfig) != "" {
 | 
			
		||||
	if b.GetString(outgoingWebhookConfig) != "" && b.GetString(tokenConfig) == "" {
 | 
			
		||||
		return "", b.sendWebhook(msg)
 | 
			
		||||
	}
 | 
			
		||||
	return b.sendRTM(msg)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,22 +39,32 @@ func (b *Btelegram) handleGroups(rmsg *config.Message, message *tgbotapi.Message
 | 
			
		||||
 | 
			
		||||
// handleForwarded handles forwarded messages
 | 
			
		||||
func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Message) {
 | 
			
		||||
	if message.ForwardFrom != nil {
 | 
			
		||||
		usernameForward := ""
 | 
			
		||||
		if b.GetBool("UseFirstName") {
 | 
			
		||||
	if message.ForwardDate == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if message.ForwardFrom == nil {
 | 
			
		||||
		rmsg.Text = "Forwarded from " + unknownUser + ": " + rmsg.Text
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	usernameForward := ""
 | 
			
		||||
	if b.GetBool("UseFirstName") {
 | 
			
		||||
		usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if usernameForward == "" {
 | 
			
		||||
		usernameForward = message.ForwardFrom.UserName
 | 
			
		||||
		if usernameForward == "" {
 | 
			
		||||
			usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
		}
 | 
			
		||||
		if usernameForward == "" {
 | 
			
		||||
			usernameForward = message.ForwardFrom.UserName
 | 
			
		||||
			if usernameForward == "" {
 | 
			
		||||
				usernameForward = message.ForwardFrom.FirstName
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if usernameForward == "" {
 | 
			
		||||
			usernameForward = unknownUser
 | 
			
		||||
		}
 | 
			
		||||
		rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if usernameForward == "" {
 | 
			
		||||
		usernameForward = unknownUser
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleQuoting handles quoting of previous messages
 | 
			
		||||
@@ -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
 | 
			
		||||
func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error {
 | 
			
		||||
	size := 0
 | 
			
		||||
@@ -254,15 +304,13 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasSuffix(name, ".webp") && b.GetBool("MediaConvertWebPToPNG") {
 | 
			
		||||
		b.Log.Debugf("WebP to PNG conversion enabled, converting %s", name)
 | 
			
		||||
		err := helper.ConvertWebPToPNG(data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			b.Log.Errorf("conversion failed: %s", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			name = strings.Replace(name, ".webp", ".png", 1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if strings.HasSuffix(name, ".tgs.webp") {
 | 
			
		||||
		b.maybeConvertTgs(&name, data)
 | 
			
		||||
	} else if strings.HasSuffix(name, ".webp") {
 | 
			
		||||
		b.maybeConvertWebp(&name, data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, "", data, b.General)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -312,6 +360,9 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error
 | 
			
		||||
	case "Markdown":
 | 
			
		||||
		b.Log.Debug("Using mode markdown")
 | 
			
		||||
		m.ParseMode = tgbotapi.ModeMarkdown
 | 
			
		||||
	case MarkdownV2:
 | 
			
		||||
		b.Log.Debug("Using mode MarkdownV2")
 | 
			
		||||
		m.ParseMode = MarkdownV2
 | 
			
		||||
	}
 | 
			
		||||
	if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
 | 
			
		||||
		b.Log.Debug("Using mode HTML - nick only")
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package btelegram
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"html"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
@@ -15,6 +16,9 @@ const (
 | 
			
		||||
	unknownUser = "unknown"
 | 
			
		||||
	HTMLFormat  = "HTML"
 | 
			
		||||
	HTMLNick    = "htmlnick"
 | 
			
		||||
	MarkdownV2  = "MarkdownV2"
 | 
			
		||||
	FormatPng   = "png"
 | 
			
		||||
	FormatWebp  = "webp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Btelegram struct {
 | 
			
		||||
@@ -24,6 +28,16 @@ type Btelegram struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -126,6 +140,10 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, er
 | 
			
		||||
		b.Log.Debug("Using mode markdown")
 | 
			
		||||
		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 {
 | 
			
		||||
		b.Log.Debug("Using mode HTML - nick only")
 | 
			
		||||
		m.Text = username + html.EscapeString(text)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,8 @@ Check:
 | 
			
		||||
// HandleError received from WhatsApp
 | 
			
		||||
func (b *Bwhatsapp) HandleError(err error) {
 | 
			
		||||
	// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
 | 
			
		||||
	if strings.Contains(err.Error(), "error processing data: received invalid data") {
 | 
			
		||||
	// ignore tag 174 errors. https://github.com/42wim/matterbridge/issues/1094
 | 
			
		||||
	if strings.Contains(err.Error(), "error processing data: received invalid data") || strings.Contains(err.Error(), "invalid string with tag 174") {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -55,6 +56,7 @@ func (b *Bwhatsapp) reconnect(err error) {
 | 
			
		||||
		err := b.conn.Restore()
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			bf.Reset()
 | 
			
		||||
			b.startedAt = uint64(time.Now().Unix())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -76,7 +78,9 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
 | 
			
		||||
	senderJID := message.Info.SenderJid
 | 
			
		||||
	if len(senderJID) == 0 {
 | 
			
		||||
		// TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved
 | 
			
		||||
		senderJID = *message.Info.Source.Participant
 | 
			
		||||
		if message.Info.Source != nil {
 | 
			
		||||
			senderJID = *message.Info.Source.Participant
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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
 | 
			
		||||
		// 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
 | 
			
		||||
		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 ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,7 @@ func (b *Bwhatsapp) Connect() error {
 | 
			
		||||
	// https://github.com/Rhymen/go-whatsapp#creating-a-connection
 | 
			
		||||
	b.Log.Debugln("Connecting to WhatsApp..")
 | 
			
		||||
	conn, err := whatsapp.NewConn(20 * time.Second)
 | 
			
		||||
	conn.SetClientVersion(0, 4, 2080)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("failed to connect to WhatsApp: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								bridge/xmpp/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								bridge/xmpp/handler.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
package bxmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/helper"
 | 
			
		||||
	"github.com/matterbridge/go-xmpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// handleDownloadAvatar downloads the avatar of userid from channel
 | 
			
		||||
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
 | 
			
		||||
// logs an error message if it fails
 | 
			
		||||
func (b *Bxmpp) handleDownloadAvatar(avatar xmpp.AvatarData) {
 | 
			
		||||
	rmsg := config.Message{
 | 
			
		||||
		Username: "system",
 | 
			
		||||
		Text:     "avatar",
 | 
			
		||||
		Channel:  b.parseChannel(avatar.From),
 | 
			
		||||
		Account:  b.Account,
 | 
			
		||||
		UserID:   avatar.From,
 | 
			
		||||
		Event:    config.EventAvatarDownload,
 | 
			
		||||
		Extra:    make(map[string][]interface{}),
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := b.avatarMap[avatar.From]; !ok {
 | 
			
		||||
		b.Log.Debugf("Avatar.From: %s", avatar.From)
 | 
			
		||||
 | 
			
		||||
		err := helper.HandleDownloadSize(b.Log, &rmsg, avatar.From+".png", int64(len(avatar.Data)), b.General)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			b.Log.Error(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		helper.HandleDownloadData(b.Log, &rmsg, avatar.From+".png", rmsg.Text, "", &avatar.Data, b.General)
 | 
			
		||||
		b.Log.Debugf("Avatar download complete")
 | 
			
		||||
		b.Remote <- rmsg
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								bridge/xmpp/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								bridge/xmpp/helpers.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package bxmpp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
 | 
			
		||||
 | 
			
		||||
// GetAvatar constructs a URL for a given user-avatar if it is available in the cache.
 | 
			
		||||
func getAvatar(av map[string]string, userid string, general *config.Protocol) string {
 | 
			
		||||
	if hash, ok := av[userid]; ok {
 | 
			
		||||
		// NOTE: This does not happen in bridge/helper/helper.go but messes up XMPP
 | 
			
		||||
		id := pathRegex.ReplaceAllString(userid, "_")
 | 
			
		||||
		return general.MediaServerDownload + "/" + hash + "/" + id + ".png"
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Bxmpp) cacheAvatar(msg *config.Message) string {
 | 
			
		||||
	fi := msg.Extra["file"][0].(config.FileInfo)
 | 
			
		||||
	/* if we have a sha we have successfully uploaded the file to the media server,
 | 
			
		||||
	so we can now cache the sha */
 | 
			
		||||
	if fi.SHA != "" {
 | 
			
		||||
		b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
 | 
			
		||||
		b.avatarMap[msg.UserID] = fi.SHA
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
@@ -23,12 +23,17 @@ type Bxmpp struct {
 | 
			
		||||
	xmppMap   map[string]string
 | 
			
		||||
	connected bool
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	avatarAvailability map[string]bool
 | 
			
		||||
	avatarMap          map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(cfg *bridge.Config) bridge.Bridger {
 | 
			
		||||
	return &Bxmpp{
 | 
			
		||||
		Config:  cfg,
 | 
			
		||||
		xmppMap: make(map[string]string),
 | 
			
		||||
		Config:             cfg,
 | 
			
		||||
		xmppMap:            make(map[string]string),
 | 
			
		||||
		avatarAvailability: make(map[string]bool),
 | 
			
		||||
		avatarMap:          make(map[string]string),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -67,8 +72,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | 
			
		||||
	if msg.Event == config.EventMsgDelete {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.Log.Debugf("=> Receiving %#v", msg)
 | 
			
		||||
 | 
			
		||||
	if msg.Event == config.EventAvatarDownload {
 | 
			
		||||
		return b.cacheAvatar(&msg), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Make a action /me of the message, prepend the username with it.
 | 
			
		||||
	// https://xmpp.org/extensions/xep-0245.html
 | 
			
		||||
	if msg.Event == config.EventUserAction {
 | 
			
		||||
		msg.Username = "/me " + msg.Username
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
 | 
			
		||||
	if msg.Extra != nil {
 | 
			
		||||
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
 | 
			
		||||
@@ -114,6 +130,9 @@ func (b *Bxmpp) createXMPP() error {
 | 
			
		||||
		ServerName:         strings.Split(b.GetString("Jid"), "@")[1],
 | 
			
		||||
		InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xmpp.DebugWriter = b.Log.Writer()
 | 
			
		||||
 | 
			
		||||
	options := xmpp.Options{
 | 
			
		||||
		Host:                         b.GetString("Server"),
 | 
			
		||||
		User:                         b.GetString("Jid"),
 | 
			
		||||
@@ -122,7 +141,6 @@ func (b *Bxmpp) createXMPP() error {
 | 
			
		||||
		StartTLS:                     true,
 | 
			
		||||
		TLSConfig:                    tc,
 | 
			
		||||
		Debug:                        b.GetBool("debug"),
 | 
			
		||||
		Logger:                       b.Log.Writer(),
 | 
			
		||||
		Session:                      true,
 | 
			
		||||
		Status:                       "",
 | 
			
		||||
		StatusMessage:                "",
 | 
			
		||||
@@ -228,6 +246,16 @@ func (b *Bxmpp) handleXMPP() error {
 | 
			
		||||
					event = config.EventTopicChange
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				available, sok := b.avatarAvailability[v.Remote]
 | 
			
		||||
				avatar := ""
 | 
			
		||||
				if !sok {
 | 
			
		||||
					b.Log.Debugf("Requesting avatar data")
 | 
			
		||||
					b.avatarAvailability[v.Remote] = false
 | 
			
		||||
					b.xc.AvatarRequestData(v.Remote)
 | 
			
		||||
				} else if available {
 | 
			
		||||
					avatar = getAvatar(b.avatarMap, v.Remote, b.General)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				msgID := v.ID
 | 
			
		||||
				if v.ReplaceID != "" {
 | 
			
		||||
					msgID = v.ReplaceID
 | 
			
		||||
@@ -237,6 +265,7 @@ func (b *Bxmpp) handleXMPP() error {
 | 
			
		||||
					Text:     v.Text,
 | 
			
		||||
					Channel:  b.parseChannel(v.Remote),
 | 
			
		||||
					Account:  b.Account,
 | 
			
		||||
					Avatar:   avatar,
 | 
			
		||||
					UserID:   v.Remote,
 | 
			
		||||
					ID:       msgID,
 | 
			
		||||
					Event:    event,
 | 
			
		||||
@@ -253,6 +282,10 @@ func (b *Bxmpp) handleXMPP() error {
 | 
			
		||||
				b.Log.Debugf("<= Message is %#v", rmsg)
 | 
			
		||||
				b.Remote <- rmsg
 | 
			
		||||
			}
 | 
			
		||||
		case xmpp.AvatarData:
 | 
			
		||||
			b.handleDownloadAvatar(v)
 | 
			
		||||
			b.avatarAvailability[v.From] = true
 | 
			
		||||
			b.Log.Debugf("Avatar for %s is now available", v.From)
 | 
			
		||||
		case xmpp.Presence:
 | 
			
		||||
			// Do nothing.
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,134 @@
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
set -u -e -x -o pipefail
 | 
			
		||||
 | 
			
		||||
go version | grep go1.14 || 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,47 +2,9 @@ package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/api"
 | 
			
		||||
	bdiscord "github.com/42wim/matterbridge/bridge/discord"
 | 
			
		||||
	bgitter "github.com/42wim/matterbridge/bridge/gitter"
 | 
			
		||||
	birc "github.com/42wim/matterbridge/bridge/irc"
 | 
			
		||||
	bkeybase "github.com/42wim/matterbridge/bridge/keybase"
 | 
			
		||||
	bmatrix "github.com/42wim/matterbridge/bridge/matrix"
 | 
			
		||||
	bmattermost "github.com/42wim/matterbridge/bridge/mattermost"
 | 
			
		||||
	bmsteams "github.com/42wim/matterbridge/bridge/msteams"
 | 
			
		||||
	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
 | 
			
		||||
	bslack "github.com/42wim/matterbridge/bridge/slack"
 | 
			
		||||
	bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
 | 
			
		||||
	bsteam "github.com/42wim/matterbridge/bridge/steam"
 | 
			
		||||
	btelegram "github.com/42wim/matterbridge/bridge/telegram"
 | 
			
		||||
	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
 | 
			
		||||
	bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
 | 
			
		||||
	bzulip "github.com/42wim/matterbridge/bridge/zulip"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	FullMap = map[string]bridge.Factory{
 | 
			
		||||
		"api":          api.New,
 | 
			
		||||
		"discord":      bdiscord.New,
 | 
			
		||||
		"gitter":       bgitter.New,
 | 
			
		||||
		"irc":          birc.New,
 | 
			
		||||
		"mattermost":   bmattermost.New,
 | 
			
		||||
		"matrix":       bmatrix.New,
 | 
			
		||||
		"rocketchat":   brocketchat.New,
 | 
			
		||||
		"slack-legacy": bslack.NewLegacy,
 | 
			
		||||
		"slack":        bslack.New,
 | 
			
		||||
		"sshchat":      bsshchat.New,
 | 
			
		||||
		"steam":        bsteam.New,
 | 
			
		||||
		"telegram":     btelegram.New,
 | 
			
		||||
		"whatsapp":     bwhatsapp.New,
 | 
			
		||||
		"xmpp":         bxmpp.New,
 | 
			
		||||
		"zulip":        bzulip.New,
 | 
			
		||||
		"keybase":      bkeybase.New,
 | 
			
		||||
		"msteams":      bmsteams.New,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UserTypingSupport = map[string]struct{}{
 | 
			
		||||
		"slack":   {},
 | 
			
		||||
		"discord": {},
 | 
			
		||||
	}
 | 
			
		||||
	FullMap           = map[string]bridge.Factory{}
 | 
			
		||||
	UserTypingSupport = map[string]struct{}{}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/brocketchat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !norocketchat
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["rocketchat"] = brocketchat.New
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gateway/bridgemap/bslack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
// +build !noslack
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	bslack "github.com/42wim/matterbridge/bridge/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["slack-legacy"] = bslack.NewLegacy
 | 
			
		||||
	FullMap["slack"] = bslack.New
 | 
			
		||||
	UserTypingSupport["slack"] = struct{}{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsshchat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !nosshchat
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["sshchat"] = bsshchat.New
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bsteam.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !nosteam
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	bsteam "github.com/42wim/matterbridge/bridge/steam"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["steam"] = bsteam.New
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/btelegram.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !notelegram
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	btelegram "github.com/42wim/matterbridge/bridge/telegram"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["telegram"] = btelegram.New
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bwhatsapp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !nowhatsapp
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["whatsapp"] = bwhatsapp.New
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bxmpp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !noxmpp
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["xmpp"] = bxmpp.New
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bzulip.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build !nozulip
 | 
			
		||||
 | 
			
		||||
package bridgemap
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	bzulip "github.com/42wim/matterbridge/bridge/zulip"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	FullMap["zulip"] = bzulip.New
 | 
			
		||||
}
 | 
			
		||||
@@ -108,7 +108,7 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
 | 
			
		||||
func (gw *Gateway) checkConfig(cfg *config.Bridge) {
 | 
			
		||||
	match := false
 | 
			
		||||
	for _, key := range gw.Router.Config.Viper().AllKeys() {
 | 
			
		||||
		if strings.HasPrefix(key, cfg.Account) {
 | 
			
		||||
		if strings.HasPrefix(key, strings.ToLower(cfg.Account)) {
 | 
			
		||||
			match = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -169,7 +169,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
 | 
			
		||||
	switch event {
 | 
			
		||||
	case config.EventAvatarDownload:
 | 
			
		||||
		// Avatar downloads are only relevant for telegram and mattermost for now
 | 
			
		||||
		if dest.Protocol != "mattermost" && dest.Protocol != "telegram" {
 | 
			
		||||
		if dest.Protocol != "mattermost" && dest.Protocol != "telegram" && dest.Protocol != "xmpp" {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	case config.EventJoinLeave:
 | 
			
		||||
@@ -179,7 +179,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
 | 
			
		||||
		}
 | 
			
		||||
	case config.EventTopicChange:
 | 
			
		||||
		// only relay topic change when used in some way on other side
 | 
			
		||||
		if dest.GetBool("ShowTopicChange") && dest.GetBool("SyncTopic") {
 | 
			
		||||
		if !dest.GetBool("ShowTopicChange") && !dest.GetBool("SyncTopic") {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										69
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,62 +5,51 @@ require (
 | 
			
		||||
	github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
 | 
			
		||||
	github.com/Jeffail/gabs v1.1.1 // indirect
 | 
			
		||||
	github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
 | 
			
		||||
	github.com/Rhymen/go-whatsapp v0.1.0
 | 
			
		||||
	github.com/d5/tengo/v2 v2.0.2
 | 
			
		||||
	github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.4.7
 | 
			
		||||
	github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
 | 
			
		||||
	github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497
 | 
			
		||||
	github.com/google/gops v0.3.6
 | 
			
		||||
	github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334
 | 
			
		||||
	github.com/d5/tengo/v2 v2.6.0
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.1
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.4.9
 | 
			
		||||
	github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81
 | 
			
		||||
	github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725
 | 
			
		||||
	github.com/google/gops v0.3.10
 | 
			
		||||
	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
 | 
			
		||||
	github.com/gorilla/schema v1.1.0
 | 
			
		||||
	github.com/gorilla/websocket v1.4.1
 | 
			
		||||
	github.com/hashicorp/golang-lru v0.5.3
 | 
			
		||||
	github.com/hpcloud/tail v1.0.0 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.4.2
 | 
			
		||||
	github.com/hashicorp/golang-lru v0.5.4
 | 
			
		||||
	github.com/jpillora/backoff v1.0.0
 | 
			
		||||
	github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e
 | 
			
		||||
	github.com/labstack/echo/v4 v4.1.13
 | 
			
		||||
	github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da
 | 
			
		||||
	github.com/labstack/echo/v4 v4.1.16
 | 
			
		||||
	github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
 | 
			
		||||
	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
 | 
			
		||||
	github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3
 | 
			
		||||
	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/go-xmpp v0.0.0-20180529212104-cd19799fba91
 | 
			
		||||
	github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
 | 
			
		||||
	github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
 | 
			
		||||
	github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
 | 
			
		||||
	github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
 | 
			
		||||
	github.com/matterbridge/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa
 | 
			
		||||
	github.com/mattermost/mattermost-server v5.5.0+incompatible
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.7 // indirect
 | 
			
		||||
	github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51
 | 
			
		||||
	github.com/matterbridge/gozulipbot v0.0.0-20200820220548-be5824faa913
 | 
			
		||||
	github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
 | 
			
		||||
	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/missdeer/golib v1.0.3
 | 
			
		||||
	github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
 | 
			
		||||
	github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
 | 
			
		||||
	github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
 | 
			
		||||
	github.com/nicksnyder/go-i18n v1.4.0 // indirect
 | 
			
		||||
	github.com/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/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
 | 
			
		||||
	github.com/rs/xid v1.2.1
 | 
			
		||||
	github.com/russross/blackfriday v1.5.2
 | 
			
		||||
	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
 | 
			
		||||
	github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98
 | 
			
		||||
	github.com/sirupsen/logrus v1.4.2
 | 
			
		||||
	github.com/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901
 | 
			
		||||
	github.com/spf13/viper v1.6.1
 | 
			
		||||
	github.com/stretchr/testify v1.4.0
 | 
			
		||||
	github.com/technoweenie/multipartstreamer v1.0.1 // indirect
 | 
			
		||||
	github.com/sirupsen/logrus v1.6.0
 | 
			
		||||
	github.com/slack-go/slack v0.6.5
 | 
			
		||||
	github.com/spf13/viper v1.7.0
 | 
			
		||||
	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/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447
 | 
			
		||||
	golang.org/x/image v0.0.0-20191214001246-9130b4cfad52
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
			
		||||
	gopkg.in/fsnotify.v1 v1.4.7 // indirect
 | 
			
		||||
	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
 | 
			
		||||
	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 | 
			
		||||
	github.com/yaegashi/msgraph.go v0.1.3
 | 
			
		||||
	github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
 | 
			
		||||
	golang.org/x/image v0.0.0-20200618115811-c13761719519
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 | 
			
		||||
	gomod.garykim.dev/nc-talk v0.0.2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43
 | 
			
		||||
 | 
			
		||||
//replace github.com/yaegashi/msgraph.go => github.com/matterbridge/msgraph.go v0.0.0-20191226214848-9e5d9c08a4e1
 | 
			
		||||
 | 
			
		||||
go 1.13
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
@@ -15,7 +16,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.17.0"
 | 
			
		||||
	version = "1.18.1"
 | 
			
		||||
	githash string
 | 
			
		||||
 | 
			
		||||
	flagConfig  = flag.String("conf", "matterbridge.toml", "config file")
 | 
			
		||||
@@ -50,6 +51,15 @@ func main() {
 | 
			
		||||
	cfg := config.NewConfig(rootLogger, *flagConfig)
 | 
			
		||||
	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)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Fatalf("Starting gateway failed: %s", err)
 | 
			
		||||
@@ -67,17 +77,31 @@ func setupLogger() *logrus.Logger {
 | 
			
		||||
		Formatter: &prefixed.TextFormatter{
 | 
			
		||||
			PrefixPadding: 13,
 | 
			
		||||
			DisableColors: true,
 | 
			
		||||
			FullTimestamp: true,
 | 
			
		||||
		},
 | 
			
		||||
		Level: logrus.InfoLevel,
 | 
			
		||||
	}
 | 
			
		||||
	if *flagDebug || os.Getenv("DEBUG") == "1" {
 | 
			
		||||
		logger.SetReportCaller(true)
 | 
			
		||||
		logger.Formatter = &prefixed.TextFormatter{
 | 
			
		||||
			PrefixPadding:   13,
 | 
			
		||||
			DisableColors:   true,
 | 
			
		||||
			FullTimestamp:   false,
 | 
			
		||||
			ForceFormatting: true,
 | 
			
		||||
			PrefixPadding: 13,
 | 
			
		||||
			DisableColors: true,
 | 
			
		||||
			FullTimestamp: false,
 | 
			
		||||
 | 
			
		||||
			CallerFormatter: func(function, file string) string {
 | 
			
		||||
				return fmt.Sprintf(" [%s:%s]", function, file)
 | 
			
		||||
			},
 | 
			
		||||
			CallerPrettyfier: func(f *runtime.Frame) (string, string) {
 | 
			
		||||
				sp := strings.SplitAfter(f.File, "/matterbridge/")
 | 
			
		||||
				filename := f.File
 | 
			
		||||
				if len(sp) > 1 {
 | 
			
		||||
					filename = sp[1]
 | 
			
		||||
				}
 | 
			
		||||
				s := strings.Split(f.Function, ".")
 | 
			
		||||
				funcName := s[len(s)-1]
 | 
			
		||||
				return funcName, fmt.Sprintf("%s:%d", filename, f.Line)
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.Level = logrus.DebugLevel
 | 
			
		||||
		logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -103,6 +103,10 @@ ColorNicks=false
 | 
			
		||||
#OPTIONAL (default empty)
 | 
			
		||||
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"]
 | 
			
		||||
 | 
			
		||||
#StripMarkdown strips markdown from messages
 | 
			
		||||
#OPTIONAL (default false)
 | 
			
		||||
StripMarkdown=false
 | 
			
		||||
 | 
			
		||||
#Nicks you want to ignore.
 | 
			
		||||
#Regular expressions supported
 | 
			
		||||
#Messages from those users will not be sent to other bridges.
 | 
			
		||||
@@ -177,6 +181,12 @@ StripNick=false
 | 
			
		||||
#OPTIONAL (default 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
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -545,6 +555,96 @@ Label=""
 | 
			
		||||
# REQUIRED
 | 
			
		||||
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
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -1113,6 +1213,11 @@ Password="yourpass"
 | 
			
		||||
#OPTIONAL (default 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
 | 
			
		||||
## Settings below can be reloaded by editing the file
 | 
			
		||||
 | 
			
		||||
@@ -1278,7 +1383,22 @@ StripNick=false
 | 
			
		||||
#OPTIONAL (default 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"
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#
 | 
			
		||||
@@ -1499,6 +1619,14 @@ MediaDownloadBlacklist=[".html$",".htm$"]
 | 
			
		||||
#OPTIONAL (default 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
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -1589,32 +1717,46 @@ enable=true
 | 
			
		||||
    # REQUIRED
 | 
			
		||||
    account="irc.freenode"
 | 
			
		||||
 | 
			
		||||
    # channel to connect on that account
 | 
			
		||||
    # How to specify them for the different bridges:
 | 
			
		||||
    # The channel key in each gateway is mapped to a similar group chat ID on the chat platform
 | 
			
		||||
    # To find the group chat ID for different platforms, refer to the table below
 | 
			
		||||
    #
 | 
			
		||||
    # irc        - #channel (# is required) (this needs to be lowercase!)
 | 
			
		||||
    # mattermost - channel (the channel name as seen in the URL, not the displayname)
 | 
			
		||||
    # gitter     - username/room
 | 
			
		||||
    # xmpp       - channel
 | 
			
		||||
    # slack      - channel (without the #)
 | 
			
		||||
    #            - ID:C123456 (where C123456 is the channel ID) does not work with webhook
 | 
			
		||||
    # discord    - channel (without the #)
 | 
			
		||||
    #            - ID:123456789 (where 123456789 is the channel ID)
 | 
			
		||||
    #               (https://github.com/42wim/matterbridge/issues/57)
 | 
			
		||||
    #            - category/channel (without the #) if you're using discord categories to group your channels
 | 
			
		||||
    # telegram   - chatid (a large negative number, eg -123456789)
 | 
			
		||||
    #             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)
 | 
			
		||||
    # rocketchat - #channel (# is required (also needed for private channels!)
 | 
			
		||||
    # matrix     - #channel:server (eg #yourchannel:matrix.org)
 | 
			
		||||
    #            - encrypted rooms are not supported in matrix
 | 
			
		||||
    # steam      - chatid (a large number).
 | 
			
		||||
    #             The number in the URL when you click "enter chat room" in the browser
 | 
			
		||||
    # whatsapp   - 48111222333-123455678999@g.us A unique group JID;
 | 
			
		||||
    #              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
 | 
			
		||||
    #              as group names might change in time and contain weird emoticons
 | 
			
		||||
    # zulip      - stream/topic:topicname (without the #)
 | 
			
		||||
    # Platform   |   Identifier name  |            Example            | Description
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #            |      channel       |            general            | Do not include the # symbol
 | 
			
		||||
    #  discord   |    channel id      |          ID:123456789         | See https://github.com/42wim/matterbridge/issues/57
 | 
			
		||||
    #            | category/channel   |          Media/gaming         | Without # symbol. If you're using discord categories to group your channels
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   gitter   |  username/room     |            general            | As seen in the gitter.im URL
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   hipchat  |    id_channel      |         example needed        | See https://www.hipchat.com/account/xmpp for the correct channel
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #    irc     |      channel       |            #general           | The # symbol is required and should be lowercase!
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    # mattermost |      channel       |            general            | This is the channel name as seen in the URL, not the display name
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   matrix   | #channel:server    |    #yourchannel:matrix.org    | Encrypted rooms are not supported in matrix
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   msteams  |      threadId      |    19:82abcxx@thread.skype    | You'll find the threadId in the URL
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    # rocketchat |      channel       |            #channel           | # is required for private channels too
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   slack    |   channel name     |            general            | Do not include the # symbol
 | 
			
		||||
    #            |    channel id      |           ID:C123456          | The underlying ID of a channel. This doesn't work with
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   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
 | 
			
		||||
    channel="#testing"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/mattermost/mattermost-server/model"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/v5/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "")
 | 
			
		||||
	mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, false, "")
 | 
			
		||||
	if resp.Error != nil {
 | 
			
		||||
		return errors.New(resp.Error.DetailedError)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"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 {
 | 
			
		||||
@@ -154,7 +154,7 @@ func (m *MMClient) initUser() error {
 | 
			
		||||
 | 
			
		||||
		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 {
 | 
			
		||||
			return resp.Error
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import (
 | 
			
		||||
	lru "github.com/hashicorp/golang-lru"
 | 
			
		||||
	"github.com/jpillora/backoff"
 | 
			
		||||
	prefixed "github.com/matterbridge/logrus-prefixed-formatter"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/model"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/v5/model"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +69,7 @@ type MMClient struct {
 | 
			
		||||
	logger     *logrus.Entry
 | 
			
		||||
	rootLogger *logrus.Logger
 | 
			
		||||
	lruCache   *lru.Cache
 | 
			
		||||
	allevents  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
func (m *MMClient) Login() error {
 | 
			
		||||
	// check if this is a first connect or a reconnection
 | 
			
		||||
@@ -220,6 +225,10 @@ func (m *MMClient) WsReceiver() {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if m.allevents {
 | 
			
		||||
				m.MessageChan <- msg
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			switch msg.Raw.Event {
 | 
			
		||||
			case model.WEBSOCKET_EVENT_USER_ADDED,
 | 
			
		||||
				model.WEBSOCKET_EVENT_USER_REMOVED,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package matterclient
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/mattermost/mattermost-server/model"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/v5/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) parseActionPost(rmsg *Message) {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/mattermost/mattermost-server/model"
 | 
			
		||||
	"github.com/mattermost/mattermost-server/v5/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (m *MMClient) GetNickName(userId string) string { //nolint:golint
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1481
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1481
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										78
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.proto
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -56,6 +56,8 @@ message Location {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Point {
 | 
			
		||||
    optional int32 xDeprecated = 1;
 | 
			
		||||
    optional int32 yDeprecated = 2;
 | 
			
		||||
    optional double x = 3;
 | 
			
		||||
    optional double y = 4;
 | 
			
		||||
}
 | 
			
		||||
@@ -93,6 +95,7 @@ message ContextInfo {
 | 
			
		||||
    optional AdReplyInfo quotedAd = 23;
 | 
			
		||||
    optional MessageKey placeholderKey = 24;
 | 
			
		||||
    optional uint32 expiration = 25;
 | 
			
		||||
    optional int64 ephemeralSettingTimestamp = 26;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SenderKeyDistributionMessage {
 | 
			
		||||
@@ -136,6 +139,11 @@ message LocationMessage {
 | 
			
		||||
    optional string name = 3;
 | 
			
		||||
    optional string address = 4;
 | 
			
		||||
    optional string url = 5;
 | 
			
		||||
    optional bool isLive = 6;
 | 
			
		||||
    optional uint32 accuracyInMeters = 7;
 | 
			
		||||
    optional float speedInMps = 8;
 | 
			
		||||
    optional uint32 degreesClockwiseFromMagneticNorth = 9;
 | 
			
		||||
    optional string comment = 11;
 | 
			
		||||
    optional bytes jpegThumbnail = 16;
 | 
			
		||||
    optional ContextInfo contextInfo = 17;
 | 
			
		||||
}
 | 
			
		||||
@@ -238,9 +246,29 @@ message ProtocolMessage {
 | 
			
		||||
    enum PROTOCOL_MESSAGE_TYPE {
 | 
			
		||||
        REVOKE = 0;
 | 
			
		||||
        EPHEMERAL_SETTING = 3;
 | 
			
		||||
        EPHEMERAL_SYNC_RESPONSE = 4;
 | 
			
		||||
        HISTORY_SYNC_NOTIFICATION = 5;
 | 
			
		||||
    }
 | 
			
		||||
    optional PROTOCOL_MESSAGE_TYPE type = 2;
 | 
			
		||||
    optional uint32 ephemeralExpiration = 4;
 | 
			
		||||
    optional int64 ephemeralSettingTimestamp = 5;
 | 
			
		||||
    optional HistorySyncNotification historySyncNotification = 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HistorySyncNotification {
 | 
			
		||||
    optional bytes fileSha256 = 1;
 | 
			
		||||
    optional uint64 fileLength = 2;
 | 
			
		||||
    optional bytes mediaKey = 3;
 | 
			
		||||
    optional bytes fileEncSha256 = 4;
 | 
			
		||||
    optional string directPath = 5;
 | 
			
		||||
    enum HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE {
 | 
			
		||||
        INITIAL_BOOTSTRAP = 0;
 | 
			
		||||
        INITIAL_STATUS_V3 = 1;
 | 
			
		||||
        FULL = 2;
 | 
			
		||||
        RECENT = 3;
 | 
			
		||||
    }
 | 
			
		||||
    optional HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE syncType = 6;
 | 
			
		||||
    optional uint32 chunkOrder = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ContactsArrayMessage {
 | 
			
		||||
@@ -355,6 +383,8 @@ message StickerMessage {
 | 
			
		||||
    optional int64 mediaKeyTimestamp = 10;
 | 
			
		||||
    optional uint32 firstFrameLength = 11;
 | 
			
		||||
    optional bytes firstFrameSidecar = 12;
 | 
			
		||||
    optional bool isAnimated = 13;
 | 
			
		||||
    optional bytes pngThumbnail = 16;
 | 
			
		||||
    optional ContextInfo contextInfo = 17;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -401,6 +431,12 @@ message TemplateButtonReplyMessage {
 | 
			
		||||
    optional uint32 selectedIndex = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message CatalogSnapshot {
 | 
			
		||||
    optional ImageMessage catalogImage = 1;
 | 
			
		||||
    optional string title = 2;
 | 
			
		||||
    optional string description = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ProductSnapshot {
 | 
			
		||||
    optional ImageMessage productImage = 1;
 | 
			
		||||
    optional string productId = 2;
 | 
			
		||||
@@ -417,6 +453,7 @@ message ProductSnapshot {
 | 
			
		||||
message ProductMessage {
 | 
			
		||||
    optional ProductSnapshot product = 1;
 | 
			
		||||
    optional string businessOwnerJid = 2;
 | 
			
		||||
    optional CatalogSnapshot catalog = 4;
 | 
			
		||||
    optional ContextInfo contextInfo = 17;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -513,6 +550,8 @@ message WebFeatures {
 | 
			
		||||
    optional WEB_FEATURES_FLAG templateMessage = 30;
 | 
			
		||||
    optional WEB_FEATURES_FLAG templateMessageInteractivity = 31;
 | 
			
		||||
    optional WEB_FEATURES_FLAG ephemeralMessages = 32;
 | 
			
		||||
    optional WEB_FEATURES_FLAG e2ENotificationSync = 33;
 | 
			
		||||
    optional WEB_FEATURES_FLAG recentStickersV2 = 34;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message TabletNotificationsInfo {
 | 
			
		||||
@@ -537,6 +576,11 @@ message WebNotificationsInfo {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message PaymentInfo {
 | 
			
		||||
    enum PAYMENT_INFO_CURRENCY {
 | 
			
		||||
        UNKNOWN_CURRENCY = 0;
 | 
			
		||||
        INR = 1;
 | 
			
		||||
    }
 | 
			
		||||
    optional PAYMENT_INFO_CURRENCY currencyDeprecated = 1;
 | 
			
		||||
    optional uint64 amount1000 = 2;
 | 
			
		||||
    optional string receiverJid = 3;
 | 
			
		||||
    enum PAYMENT_INFO_STATUS {
 | 
			
		||||
@@ -559,6 +603,37 @@ message PaymentInfo {
 | 
			
		||||
    optional uint64 expiryTimestamp = 7;
 | 
			
		||||
    optional bool futureproofed = 8;
 | 
			
		||||
    optional string currency = 9;
 | 
			
		||||
    enum PAYMENT_INFO_TXNSTATUS {
 | 
			
		||||
        UNKNOWN = 0;
 | 
			
		||||
        PENDING_SETUP = 1;
 | 
			
		||||
        PENDING_RECEIVER_SETUP = 2;
 | 
			
		||||
        INIT = 3;
 | 
			
		||||
        SUCCESS = 4;
 | 
			
		||||
        COMPLETED = 5;
 | 
			
		||||
        FAILED = 6;
 | 
			
		||||
        FAILED_RISK = 7;
 | 
			
		||||
        FAILED_PROCESSING = 8;
 | 
			
		||||
        FAILED_RECEIVER_PROCESSING = 9;
 | 
			
		||||
        FAILED_DA = 10;
 | 
			
		||||
        FAILED_DA_FINAL = 11;
 | 
			
		||||
        REFUNDED_TXN = 12;
 | 
			
		||||
        REFUND_FAILED = 13;
 | 
			
		||||
        REFUND_FAILED_PROCESSING = 14;
 | 
			
		||||
        REFUND_FAILED_DA = 15;
 | 
			
		||||
        EXPIRED_TXN = 16;
 | 
			
		||||
        AUTH_CANCELED = 17;
 | 
			
		||||
        AUTH_CANCEL_FAILED_PROCESSING = 18;
 | 
			
		||||
        AUTH_CANCEL_FAILED = 19;
 | 
			
		||||
        COLLECT_INIT = 20;
 | 
			
		||||
        COLLECT_SUCCESS = 21;
 | 
			
		||||
        COLLECT_FAILED = 22;
 | 
			
		||||
        COLLECT_FAILED_RISK = 23;
 | 
			
		||||
        COLLECT_REJECTED = 24;
 | 
			
		||||
        COLLECT_EXPIRED = 25;
 | 
			
		||||
        COLLECT_CANCELED = 26;
 | 
			
		||||
        COLLECT_CANCELLING = 27;
 | 
			
		||||
    }
 | 
			
		||||
    optional PAYMENT_INFO_TXNSTATUS txnStatus = 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message WebMessageInfo {
 | 
			
		||||
@@ -668,4 +743,5 @@ message WebMessageInfo {
 | 
			
		||||
    optional PaymentInfo quotedPaymentInfo = 31;
 | 
			
		||||
    optional uint64 ephemeralStartTimestamp = 32;
 | 
			
		||||
    optional uint32 ephemeralDuration = 33;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -90,6 +90,7 @@ type Conn struct {
 | 
			
		||||
 | 
			
		||||
	longClientName  string
 | 
			
		||||
	shortClientName string
 | 
			
		||||
	clientVersion   string
 | 
			
		||||
 | 
			
		||||
	loginSessionLock sync.RWMutex
 | 
			
		||||
	Proxy            func(*http.Request) (*url.URL, error)
 | 
			
		||||
@@ -121,6 +122,7 @@ func NewConn(timeout time.Duration) (*Conn, error) {
 | 
			
		||||
 | 
			
		||||
		longClientName:  "github.com/rhymen/go-whatsapp",
 | 
			
		||||
		shortClientName: "go-whatsapp",
 | 
			
		||||
		clientVersion:   "0.1.0",
 | 
			
		||||
	}
 | 
			
		||||
	return wac, wac.connect()
 | 
			
		||||
}
 | 
			
		||||
@@ -135,6 +137,7 @@ func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL
 | 
			
		||||
 | 
			
		||||
		longClientName:  "github.com/rhymen/go-whatsapp",
 | 
			
		||||
		shortClientName: "go-whatsapp",
 | 
			
		||||
		clientVersion:   "0.1.0",
 | 
			
		||||
		Proxy:           proxy,
 | 
			
		||||
	}
 | 
			
		||||
	return wac, wac.connect()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@ require (
 | 
			
		||||
	github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect
 | 
			
		||||
	github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect
 | 
			
		||||
	github.com/golang/protobuf v1.3.0
 | 
			
		||||
	github.com/gorilla/websocket v1.4.0
 | 
			
		||||
	github.com/gorilla/websocket v1.4.1
 | 
			
		||||
	github.com/pkg/errors v0.8.1
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -12,8 +12,9 @@ github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
 | 
			
		||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
 | 
			
		||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										116
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -10,10 +10,8 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"mime/multipart"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/Rhymen/go-whatsapp/crypto/cbc"
 | 
			
		||||
@@ -95,7 +93,50 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) {
 | 
			
		||||
	return data[:n-10], data[n-10 : n], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
 | 
			
		||||
type MediaConn struct {
 | 
			
		||||
	Status    int `json:"status"`
 | 
			
		||||
	MediaConn struct {
 | 
			
		||||
		Auth  string `json:"auth"`
 | 
			
		||||
		TTL   int    `json:"ttl"`
 | 
			
		||||
		Hosts []struct {
 | 
			
		||||
			Hostname string   `json:"hostname"`
 | 
			
		||||
			IPs      []string `json:"ips"`
 | 
			
		||||
		} `json:"hosts"`
 | 
			
		||||
	} `json:"media_conn"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) {
 | 
			
		||||
	queryReq := []interface{}{"query", "mediaConn"}
 | 
			
		||||
	ch, err := wac.writeJson(queryReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var resp MediaConn
 | 
			
		||||
	select {
 | 
			
		||||
	case r := <-ch:
 | 
			
		||||
		if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
			
		||||
			return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	case <-time.After(wac.msgTimeout):
 | 
			
		||||
		return "", "", 0, fmt.Errorf("query media conn timed out")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.Status != 200 {
 | 
			
		||||
		return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return resp.MediaConn.Hosts[0].Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mediaTypeMap = map[MediaType]string{
 | 
			
		||||
	MediaImage: "/mms/image",
 | 
			
		||||
	MediaVideo: "/mms/video",
 | 
			
		||||
	MediaDocument: "/mms/document",
 | 
			
		||||
	MediaAudio: "/mms/audio",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
 | 
			
		||||
	data, err := ioutil.ReadAll(reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", nil, nil, nil, 0, err
 | 
			
		||||
@@ -128,67 +169,30 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK
 | 
			
		||||
	sha.Write(append(enc, mac...))
 | 
			
		||||
	fileEncSha256 = sha.Sum(nil)
 | 
			
		||||
 | 
			
		||||
	var filetype string
 | 
			
		||||
	switch appInfo {
 | 
			
		||||
	case MediaImage:
 | 
			
		||||
		filetype = "image"
 | 
			
		||||
	case MediaAudio:
 | 
			
		||||
		filetype = "audio"
 | 
			
		||||
	case MediaDocument:
 | 
			
		||||
		filetype = "document"
 | 
			
		||||
	case MediaVideo:
 | 
			
		||||
		filetype = "video"
 | 
			
		||||
	hostname, auth, _, err := wac.queryMediaConn()
 | 
			
		||||
	token := base64.URLEncoding.EncodeToString(fileEncSha256)
 | 
			
		||||
	q := url.Values{
 | 
			
		||||
		"auth":  []string{auth},
 | 
			
		||||
		"token": []string{token},
 | 
			
		||||
	}
 | 
			
		||||
	path := mediaTypeMap[appInfo]
 | 
			
		||||
	uploadURL := url.URL{
 | 
			
		||||
		Scheme:   "https",
 | 
			
		||||
		Host:     hostname,
 | 
			
		||||
		Path:     fmt.Sprintf("%s/%s", path, token),
 | 
			
		||||
		RawQuery: q.Encode(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
 | 
			
		||||
	ch, err := wac.writeJson(uploadReq)
 | 
			
		||||
	body := bytes.NewReader(append(enc, mac...))
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("POST", uploadURL.String(), body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", nil, nil, nil, 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var resp map[string]interface{}
 | 
			
		||||
	select {
 | 
			
		||||
	case r := <-ch:
 | 
			
		||||
		if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
			
		||||
			return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	case <-time.After(wac.msgTimeout):
 | 
			
		||||
		return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if int(resp["status"].(float64)) != 200 {
 | 
			
		||||
		return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	w := multipart.NewWriter(&b)
 | 
			
		||||
	hashWriter, err := w.CreateFormField("hash")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256)))
 | 
			
		||||
 | 
			
		||||
	fileWriter, err := w.CreateFormFile("file", "blob")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	io.Copy(fileWriter, bytes.NewReader(append(enc, mac...)))
 | 
			
		||||
	err = w.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "%v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("POST", resp["url"].(string), &b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", nil, nil, nil, 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req.Header.Set("Content-Type", w.FormDataContentType())
 | 
			
		||||
	req.Header.Set("Origin", "https://web.whatsapp.com")
 | 
			
		||||
	req.Header.Set("Referer", "https://web.whatsapp.com/")
 | 
			
		||||
 | 
			
		||||
	req.URL.Query().Set("f", "j")
 | 
			
		||||
 | 
			
		||||
	client := &http.Client{}
 | 
			
		||||
	// Submit the request
 | 
			
		||||
	res, err := client.Do(req)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -15,7 +15,10 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (wac *Conn) readPump() {
 | 
			
		||||
	defer wac.wg.Done()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		wac.wg.Done()
 | 
			
		||||
		_, _ = wac.Disconnect()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var readErr error
 | 
			
		||||
	var msgType int
 | 
			
		||||
@@ -31,7 +34,6 @@ func (wac *Conn) readPump() {
 | 
			
		||||
		case <-readerFound:
 | 
			
		||||
			if readErr != nil {
 | 
			
		||||
				wac.handle(&ErrConnectionFailed{Err: readErr})
 | 
			
		||||
				_, _ = wac.Disconnect()
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			msg, err := ioutil.ReadAll(reader)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//represents the WhatsAppWeb client version
 | 
			
		||||
var waVersion = []int{0, 3, 3324}
 | 
			
		||||
var waVersion = []int{0, 4, 2080}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Session contains session individual information. To be able to resume the connection without scanning the qr code
 | 
			
		||||
@@ -107,10 +107,10 @@ func CheckCurrentServerVersion() ([]int, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b64ClientId := base64.StdEncoding.EncodeToString(clientId)
 | 
			
		||||
	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true}
 | 
			
		||||
	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true}
 | 
			
		||||
	loginChan, err := wac.writeJson(login)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error writing login", err)
 | 
			
		||||
		return nil, fmt.Errorf("error writing login: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Retrieve an answer from the websocket
 | 
			
		||||
@@ -123,7 +123,7 @@ func CheckCurrentServerVersion() ([]int, error) {
 | 
			
		||||
 | 
			
		||||
	var resp map[string]interface{}
 | 
			
		||||
	if err = json.Unmarshal([]byte(r), &resp); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error decoding login", err)
 | 
			
		||||
		return nil, fmt.Errorf("error decoding login: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Take the curr property as X.Y.Z and split it into as int slice
 | 
			
		||||
@@ -141,17 +141,17 @@ func CheckCurrentServerVersion() ([]int, error) {
 | 
			
		||||
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
 | 
			
		||||
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
 | 
			
		||||
*/
 | 
			
		||||
func (wac *Conn) SetClientName(long, short string) error {
 | 
			
		||||
func (wac *Conn) SetClientName(long, short, version string) error {
 | 
			
		||||
	if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
 | 
			
		||||
		return fmt.Errorf("cannot change client name after logging in")
 | 
			
		||||
	}
 | 
			
		||||
	wac.longClientName, wac.shortClientName = long, short
 | 
			
		||||
	wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
SetClientVersion sets WhatsApp client version
 | 
			
		||||
Default value is 0.3.3324
 | 
			
		||||
Default value is 0.4.2080
 | 
			
		||||
*/
 | 
			
		||||
func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
 | 
			
		||||
	waVersion = []int{major, minor, patch}
 | 
			
		||||
@@ -213,7 +213,7 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session.ClientId = base64.StdEncoding.EncodeToString(clientId)
 | 
			
		||||
	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
 | 
			
		||||
	login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true}
 | 
			
		||||
	loginChan, err := wac.writeJson(login)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return session, fmt.Errorf("error writing login: %v\n", err)
 | 
			
		||||
@@ -369,7 +369,7 @@ func (wac *Conn) Restore() error {
 | 
			
		||||
	wac.listener.Unlock()
 | 
			
		||||
 | 
			
		||||
	//admin init
 | 
			
		||||
	init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true}
 | 
			
		||||
	init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true}
 | 
			
		||||
	initChan, err := wac.writeJson(init)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error writing admin init: %v\n", err)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/d5/tengo/v2/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/d5/tengo/v2/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,9 +11,10 @@ builds:
 | 
			
		||||
      - darwin
 | 
			
		||||
      - linux
 | 
			
		||||
      - windows
 | 
			
		||||
archive:
 | 
			
		||||
  files:
 | 
			
		||||
    - none*
 | 
			
		||||
archives:
 | 
			
		||||
  -
 | 
			
		||||
    files:
 | 
			
		||||
      - none*
 | 
			
		||||
checksum:
 | 
			
		||||
  name_template: 'checksums.txt'
 | 
			
		||||
changelog:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/d5/tengo/v2/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/d5/tengo/v2/Makefile
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,6 +6,7 @@ lint:
 | 
			
		||||
 | 
			
		||||
test: generate lint
 | 
			
		||||
	go test -race -cover ./...
 | 
			
		||||
	go run ./cmd/tengo -resolve ./testdata/cli/test.tengo
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
	go fmt ./...
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,9 +5,8 @@
 | 
			
		||||
# The Tengo Language
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/d5/tengo)
 | 
			
		||||

 | 
			
		||||
[](https://goreportcard.com/report/github.com/d5/tengo)
 | 
			
		||||
[](https://circleci.com/gh/d5/tengo)
 | 
			
		||||
[](https://sourcegraph.com/github.com/d5/tengo?badge)
 | 
			
		||||
 | 
			
		||||
**Tengo is a small, dynamic, fast, secure script language for Go.** 
 | 
			
		||||
 | 
			
		||||
@@ -52,19 +51,21 @@ fmt.println(sum("", [1, 2, 3]))  // "123"
 | 
			
		||||
 | 
			
		||||
## Benchmark
 | 
			
		||||
 | 
			
		||||
| | fib(35) | fibt(35) |  Type  |
 | 
			
		||||
| | fib(35) | fibt(35) |  Language (Type)  |
 | 
			
		||||
| :--- |    ---: |     ---: |  :---: |
 | 
			
		||||
| Go | `48ms` | `3ms` | Go (native) |
 | 
			
		||||
| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go |
 | 
			
		||||
| Lua | `1,416ms` | `3ms` | Lua (native) |
 | 
			
		||||
| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go |
 | 
			
		||||
| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go |
 | 
			
		||||
| Python | `2,588ms` | `26ms` | Python (native) |
 | 
			
		||||
| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go |
 | 
			
		||||
| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go |
 | 
			
		||||
| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go |
 | 
			
		||||
| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go |
 | 
			
		||||
| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go |
 | 
			
		||||
| [**Tengo**](https://github.com/d5/tengo) | `2,931ms` | `4ms` | Tengo (VM) |
 | 
			
		||||
| [go-lua](https://github.com/Shopify/go-lua) | `4,824ms` | `4ms` | Lua (VM) |
 | 
			
		||||
| [GopherLua](https://github.com/yuin/gopher-lua) | `5,365ms` | `4ms` | Lua (VM) |
 | 
			
		||||
| [goja](https://github.com/dop251/goja) | `5,533ms` | `5ms` | JavaScript (VM) |
 | 
			
		||||
| [starlark-go](https://github.com/google/starlark-go) | `11,495ms` | `5ms` | Starlark (Interpreter) |
 | 
			
		||||
| [Yaegi](https://github.com/containous/yaegi) | `15,645ms` | `12ms` | Yaegi (Interpreter) |
 | 
			
		||||
| [gpython](https://github.com/go-python/gpython) | `16,322ms` | `5ms` | Python (Interpreter) |
 | 
			
		||||
| [otto](https://github.com/robertkrimen/otto) | `73,093ms` | `10ms` | JavaScript (Interpreter) |
 | 
			
		||||
| [Anko](https://github.com/mattn/anko) | `79,809ms` | `8ms` | Anko (Interpreter) |
 | 
			
		||||
| - | - | - | - |
 | 
			
		||||
| Go | `53ms` | `3ms` | Go (Native) |
 | 
			
		||||
| Lua | `1,612ms` | `3ms` | Lua (Native) |
 | 
			
		||||
| Python | `2,632ms` | `23ms` | Python 2 (Native) |
 | 
			
		||||
 | 
			
		||||
_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo):
 | 
			
		||||
Fibonacci(35)_  
 | 
			
		||||
@@ -75,6 +76,10 @@ _* See [here](https://github.com/d5/tengobench) for commands/codes used_
 | 
			
		||||
 | 
			
		||||
## Quick Start
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
go get github.com/d5/tengo/v2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A simple Go example code that compiles/runs Tengo script code with some input/output values:
 | 
			
		||||
 | 
			
		||||
```golang
 | 
			
		||||
@@ -133,3 +138,10 @@ each([a, b, c, d], func(x) {
 | 
			
		||||
- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
 | 
			
		||||
- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
 | 
			
		||||
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
 | 
			
		||||
- Syntax Highlighters: [VSCode](https://github.com/lissein/vscode-tengo), [Atom](https://github.com/d5/tengo-atom)
 | 
			
		||||
- **Why the name Tengo?** It's from [1Q84](https://en.wikipedia.org/wiki/1Q84).
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
:hearts: Like writing Go code? Come work at Skool. [We're hiring!](https://jobs.lever.co/skool)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								vendor/github.com/d5/tengo/v2/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/d5/tengo/v2/builtins.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,6 +13,14 @@ var builtinFuncs = []*BuiltinFunction{
 | 
			
		||||
		Name:  "append",
 | 
			
		||||
		Value: builtinAppend,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		Name:  "delete",
 | 
			
		||||
		Value: builtinDelete,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		Name:  "splice",
 | 
			
		||||
		Value: builtinSplice,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		Name:  "string",
 | 
			
		||||
		Value: builtinString,
 | 
			
		||||
@@ -500,3 +508,104 @@ func builtinAppend(args ...Object) (Object, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// builtinDelete deletes Map keys
 | 
			
		||||
// usage: delete(map, "key")
 | 
			
		||||
// key must be a string
 | 
			
		||||
func builtinDelete(args ...Object) (Object, error) {
 | 
			
		||||
	argsLen := len(args)
 | 
			
		||||
	if argsLen != 2 {
 | 
			
		||||
		return nil, ErrWrongNumArguments
 | 
			
		||||
	}
 | 
			
		||||
	switch arg := args[0].(type) {
 | 
			
		||||
	case *Map:
 | 
			
		||||
		if key, ok := args[1].(*String); ok {
 | 
			
		||||
			delete(arg.Value, key.Value)
 | 
			
		||||
			return UndefinedValue, nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil, ErrInvalidArgumentType{
 | 
			
		||||
			Name:     "second",
 | 
			
		||||
			Expected: "string",
 | 
			
		||||
			Found:    args[1].TypeName(),
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, ErrInvalidArgumentType{
 | 
			
		||||
			Name:     "first",
 | 
			
		||||
			Expected: "map",
 | 
			
		||||
			Found:    arg.TypeName(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// builtinSplice deletes and changes given Array, returns deleted items.
 | 
			
		||||
// usage:
 | 
			
		||||
// deleted_items := splice(array[,start[,delete_count[,item1[,item2[,...]]]])
 | 
			
		||||
func builtinSplice(args ...Object) (Object, error) {
 | 
			
		||||
	argsLen := len(args)
 | 
			
		||||
	if argsLen == 0 {
 | 
			
		||||
		return nil, ErrWrongNumArguments
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	array, ok := args[0].(*Array)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, ErrInvalidArgumentType{
 | 
			
		||||
			Name:     "first",
 | 
			
		||||
			Expected: "array",
 | 
			
		||||
			Found:    args[0].TypeName(),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	arrayLen := len(array.Value)
 | 
			
		||||
 | 
			
		||||
	var startIdx int
 | 
			
		||||
	if argsLen > 1 {
 | 
			
		||||
		arg1, ok := args[1].(*Int)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, ErrInvalidArgumentType{
 | 
			
		||||
				Name:     "second",
 | 
			
		||||
				Expected: "int",
 | 
			
		||||
				Found:    args[1].TypeName(),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		startIdx = int(arg1.Value)
 | 
			
		||||
		if startIdx < 0 || startIdx > arrayLen {
 | 
			
		||||
			return nil, ErrIndexOutOfBounds
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delCount := len(array.Value)
 | 
			
		||||
	if argsLen > 2 {
 | 
			
		||||
		arg2, ok := args[2].(*Int)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, ErrInvalidArgumentType{
 | 
			
		||||
				Name:     "third",
 | 
			
		||||
				Expected: "int",
 | 
			
		||||
				Found:    args[2].TypeName(),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		delCount = int(arg2.Value)
 | 
			
		||||
		if delCount < 0 {
 | 
			
		||||
			return nil, ErrIndexOutOfBounds
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// if count of to be deleted items is bigger than expected, truncate it
 | 
			
		||||
	if startIdx+delCount > arrayLen {
 | 
			
		||||
		delCount = arrayLen - startIdx
 | 
			
		||||
	}
 | 
			
		||||
	// delete items
 | 
			
		||||
	endIdx := startIdx + delCount
 | 
			
		||||
	deleted := append([]Object{}, array.Value[startIdx:endIdx]...)
 | 
			
		||||
 | 
			
		||||
	head := array.Value[:startIdx]
 | 
			
		||||
	var items []Object
 | 
			
		||||
	if argsLen > 3 {
 | 
			
		||||
		items = make([]Object, 0, argsLen-3)
 | 
			
		||||
		for i := 3; i < argsLen; i++ {
 | 
			
		||||
			items = append(items, args[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	items = append(items, array.Value[endIdx:]...)
 | 
			
		||||
	array.Value = append(head, items...)
 | 
			
		||||
 | 
			
		||||
	// return deleted items
 | 
			
		||||
	return &Array{Value: deleted}, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/d5/tengo/v2/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/d5/tengo/v2/bytecode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -97,6 +97,7 @@ func (b *Bytecode) RemoveDuplicates() {
 | 
			
		||||
	var deduped []Object
 | 
			
		||||
 | 
			
		||||
	indexMap := make(map[int]int) // mapping from old constant index to new index
 | 
			
		||||
	fns := make(map[*CompiledFunction]int)
 | 
			
		||||
	ints := make(map[int64]int)
 | 
			
		||||
	strings := make(map[string]int)
 | 
			
		||||
	floats := make(map[float64]int)
 | 
			
		||||
@@ -106,9 +107,14 @@ func (b *Bytecode) RemoveDuplicates() {
 | 
			
		||||
	for curIdx, c := range b.Constants {
 | 
			
		||||
		switch c := c.(type) {
 | 
			
		||||
		case *CompiledFunction:
 | 
			
		||||
			// add to deduped list
 | 
			
		||||
			indexMap[curIdx] = len(deduped)
 | 
			
		||||
			deduped = append(deduped, c)
 | 
			
		||||
			if newIdx, ok := fns[c]; ok {
 | 
			
		||||
				indexMap[curIdx] = newIdx
 | 
			
		||||
			} else {
 | 
			
		||||
				newIdx = len(deduped)
 | 
			
		||||
				fns[c] = newIdx
 | 
			
		||||
				indexMap[curIdx] = newIdx
 | 
			
		||||
				deduped = append(deduped, c)
 | 
			
		||||
			}
 | 
			
		||||
		case *ImmutableMap:
 | 
			
		||||
			modName := inferModuleName(c)
 | 
			
		||||
			newIdx, ok := immutableMaps[modName]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
	parent          *Compiler
 | 
			
		||||
	modulePath      string
 | 
			
		||||
	importDir       string
 | 
			
		||||
	constants       []Object
 | 
			
		||||
	symbolTable     *SymbolTable
 | 
			
		||||
	scopes          []compilationScope
 | 
			
		||||
@@ -505,7 +506,11 @@ func (c *Compiler) Compile(node parser.Node) error {
 | 
			
		||||
				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:
 | 
			
		||||
		if node.ModuleName == "" {
 | 
			
		||||
			return c.errorf(node, "empty module name")
 | 
			
		||||
@@ -520,12 +525,12 @@ func (c *Compiler) Compile(node parser.Node) error {
 | 
			
		||||
			switch v := v.(type) {
 | 
			
		||||
			case []byte: // module written in Tengo
 | 
			
		||||
				compiled, err := c.compileModule(node,
 | 
			
		||||
					node.ModuleName, node.ModuleName, v)
 | 
			
		||||
					node.ModuleName, v, false)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				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
 | 
			
		||||
				c.emit(node, parser.OpConstant, c.addConstant(v))
 | 
			
		||||
			default:
 | 
			
		||||
@@ -537,29 +542,25 @@ func (c *Compiler) Compile(node parser.Node) error {
 | 
			
		||||
				moduleName += ".tengo"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			modulePath, err := filepath.Abs(moduleName)
 | 
			
		||||
			modulePath, err := filepath.Abs(
 | 
			
		||||
				filepath.Join(c.importDir, moduleName))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return c.errorf(node, "module file path error: %s",
 | 
			
		||||
					err.Error())
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := c.checkCyclicImports(node, modulePath); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			moduleSrc, err := ioutil.ReadFile(moduleName)
 | 
			
		||||
			moduleSrc, err := ioutil.ReadFile(modulePath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return c.errorf(node, "module file read error: %s",
 | 
			
		||||
					err.Error())
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			compiled, err := c.compileModule(node,
 | 
			
		||||
				moduleName, modulePath, moduleSrc)
 | 
			
		||||
			compiled, err := c.compileModule(node, modulePath, moduleSrc, true)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			c.emit(node, parser.OpConstant, c.addConstant(compiled))
 | 
			
		||||
			c.emit(node, parser.OpCall, 0)
 | 
			
		||||
			c.emit(node, parser.OpCall, 0, 0)
 | 
			
		||||
		} else {
 | 
			
		||||
			return c.errorf(node, "module '%s' not found", node.ModuleName)
 | 
			
		||||
		}
 | 
			
		||||
@@ -634,6 +635,11 @@ func (c *Compiler) EnableFileImport(enable bool) {
 | 
			
		||||
	c.allowFileImport = enable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetImportDir sets the initial import directory path for file imports.
 | 
			
		||||
func (c *Compiler) SetImportDir(dir string) {
 | 
			
		||||
	c.importDir = dir
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Compiler) compileAssign(
 | 
			
		||||
	node parser.Node,
 | 
			
		||||
	lhs, rhs []parser.Expr,
 | 
			
		||||
@@ -847,8 +853,8 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
 | 
			
		||||
	//     ... body ...
 | 
			
		||||
	//   }
 | 
			
		||||
	//
 | 
			
		||||
	// ":it" is a local variable but will be conflict with other user variables
 | 
			
		||||
	// because character ":" is not allowed.
 | 
			
		||||
	// ":it" is a local variable but it will not conflict with other user variables
 | 
			
		||||
	// because character ":" is not allowed in the variable names.
 | 
			
		||||
 | 
			
		||||
	// init
 | 
			
		||||
	//   :it = iterator(iterable)
 | 
			
		||||
@@ -893,6 +899,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
 | 
			
		||||
		if keySymbol.Scope == ScopeGlobal {
 | 
			
		||||
			c.emit(stmt, parser.OpSetGlobal, keySymbol.Index)
 | 
			
		||||
		} else {
 | 
			
		||||
			keySymbol.LocalAssigned = true
 | 
			
		||||
			c.emit(stmt, parser.OpDefineLocal, keySymbol.Index)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -909,6 +916,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
 | 
			
		||||
		if valueSymbol.Scope == ScopeGlobal {
 | 
			
		||||
			c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index)
 | 
			
		||||
		} else {
 | 
			
		||||
			valueSymbol.LocalAssigned = true
 | 
			
		||||
			c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -955,8 +963,9 @@ func (c *Compiler) checkCyclicImports(
 | 
			
		||||
 | 
			
		||||
func (c *Compiler) compileModule(
 | 
			
		||||
	node parser.Node,
 | 
			
		||||
	moduleName, modulePath string,
 | 
			
		||||
	modulePath string,
 | 
			
		||||
	src []byte,
 | 
			
		||||
	isFile bool,
 | 
			
		||||
) (*CompiledFunction, error) {
 | 
			
		||||
	if err := c.checkCyclicImports(node, modulePath); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -967,7 +976,7 @@ func (c *Compiler) compileModule(
 | 
			
		||||
		return compiledModule, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	modFile := c.file.Set().AddFile(moduleName, -1, len(src))
 | 
			
		||||
	modFile := c.file.Set().AddFile(modulePath, -1, len(src))
 | 
			
		||||
	p := parser.NewParser(modFile, src, nil)
 | 
			
		||||
	file, err := p.ParseFile()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -984,7 +993,7 @@ func (c *Compiler) compileModule(
 | 
			
		||||
	symbolTable = symbolTable.Fork(false)
 | 
			
		||||
 | 
			
		||||
	// compile module
 | 
			
		||||
	moduleCompiler := c.fork(modFile, modulePath, symbolTable)
 | 
			
		||||
	moduleCompiler := c.fork(modFile, modulePath, symbolTable, isFile)
 | 
			
		||||
	if err := moduleCompiler.Compile(file); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -1082,10 +1091,16 @@ func (c *Compiler) fork(
 | 
			
		||||
	file *parser.SourceFile,
 | 
			
		||||
	modulePath string,
 | 
			
		||||
	symbolTable *SymbolTable,
 | 
			
		||||
	isFile bool,
 | 
			
		||||
) *Compiler {
 | 
			
		||||
	child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
 | 
			
		||||
	child.modulePath = modulePath // module file path
 | 
			
		||||
	child.parent = c              // parent to set to current compiler
 | 
			
		||||
	child.allowFileImport = c.allowFileImport
 | 
			
		||||
	child.importDir = c.importDir
 | 
			
		||||
	if isFile && c.importDir != "" {
 | 
			
		||||
		child.importDir = filepath.Dir(modulePath)
 | 
			
		||||
	}
 | 
			
		||||
	return child
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1192,6 +1207,7 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
 | 
			
		||||
	var lastOp parser.Opcode
 | 
			
		||||
	var appendReturn bool
 | 
			
		||||
	endPos := len(c.scopes[c.scopeIndex].Instructions)
 | 
			
		||||
	newEndPost := len(newInsts)
 | 
			
		||||
	iterateInstructions(newInsts,
 | 
			
		||||
		func(pos int, opcode parser.Opcode, operands []int) bool {
 | 
			
		||||
			switch opcode {
 | 
			
		||||
@@ -1204,6 +1220,8 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
 | 
			
		||||
				} else if endPos == operands[0] {
 | 
			
		||||
					// there's a jump instruction that jumps to the end of
 | 
			
		||||
					// function compiler should append "return".
 | 
			
		||||
					copy(newInsts[pos:],
 | 
			
		||||
						MakeInstruction(opcode, newEndPost))
 | 
			
		||||
					appendReturn = true
 | 
			
		||||
				} else {
 | 
			
		||||
					panic(fmt.Errorf("invalid jump position: %d", newDst))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/d5/tengo/v2/objects.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/d5/tengo/v2/objects.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1342,6 +1342,38 @@ func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
 | 
			
		||||
			}
 | 
			
		||||
			return &String{Value: o.Value + rhsStr}, nil
 | 
			
		||||
		}
 | 
			
		||||
	case token.Less:
 | 
			
		||||
		switch rhs := rhs.(type) {
 | 
			
		||||
		case *String:
 | 
			
		||||
			if o.Value < rhs.Value {
 | 
			
		||||
				return TrueValue, nil
 | 
			
		||||
			}
 | 
			
		||||
			return FalseValue, nil
 | 
			
		||||
		}
 | 
			
		||||
	case token.LessEq:
 | 
			
		||||
		switch rhs := rhs.(type) {
 | 
			
		||||
		case *String:
 | 
			
		||||
			if o.Value <= rhs.Value {
 | 
			
		||||
				return TrueValue, nil
 | 
			
		||||
			}
 | 
			
		||||
			return FalseValue, nil
 | 
			
		||||
		}
 | 
			
		||||
	case token.Greater:
 | 
			
		||||
		switch rhs := rhs.(type) {
 | 
			
		||||
		case *String:
 | 
			
		||||
			if o.Value > rhs.Value {
 | 
			
		||||
				return TrueValue, nil
 | 
			
		||||
			}
 | 
			
		||||
			return FalseValue, nil
 | 
			
		||||
		}
 | 
			
		||||
	case token.GreaterEq:
 | 
			
		||||
		switch rhs := rhs.(type) {
 | 
			
		||||
		case *String:
 | 
			
		||||
			if o.Value >= rhs.Value {
 | 
			
		||||
				return TrueValue, nil
 | 
			
		||||
			}
 | 
			
		||||
			return FalseValue, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, ErrInvalidOperator
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/d5/tengo/v2/parser/expr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/d5/tengo/v2/parser/expr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -111,10 +111,11 @@ func (e *BoolLit) String() string {
 | 
			
		||||
 | 
			
		||||
// CallExpr represents a function call expression.
 | 
			
		||||
type CallExpr struct {
 | 
			
		||||
	Func   Expr
 | 
			
		||||
	LParen Pos
 | 
			
		||||
	Args   []Expr
 | 
			
		||||
	RParen Pos
 | 
			
		||||
	Func     Expr
 | 
			
		||||
	LParen   Pos
 | 
			
		||||
	Args     []Expr
 | 
			
		||||
	Ellipsis Pos
 | 
			
		||||
	RParen   Pos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *CallExpr) exprNode() {}
 | 
			
		||||
@@ -134,6 +135,9 @@ func (e *CallExpr) String() string {
 | 
			
		||||
	for _, e := range e.Args {
 | 
			
		||||
		args = append(args, e.String())
 | 
			
		||||
	}
 | 
			
		||||
	if len(args) > 0 && e.Ellipsis.IsValid() {
 | 
			
		||||
		args[len(args)-1] = args[len(args)-1] + "..."
 | 
			
		||||
	}
 | 
			
		||||
	return e.Func.String() + "(" + strings.Join(args, ", ") + ")"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/d5/tengo/v2/parser/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/d5/tengo/v2/parser/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -120,7 +120,7 @@ var OpcodeOperands = [...][]int{
 | 
			
		||||
	OpImmutable:     {},
 | 
			
		||||
	OpIndex:         {},
 | 
			
		||||
	OpSliceIndex:    {},
 | 
			
		||||
	OpCall:          {1},
 | 
			
		||||
	OpCall:          {1, 1},
 | 
			
		||||
	OpReturn:        {1},
 | 
			
		||||
	OpGetLocal:      {1},
 | 
			
		||||
	OpSetLocal:      {1},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/d5/tengo/v2/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/d5/tengo/v2/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -270,9 +270,13 @@ func (p *Parser) parseCall(x Expr) *CallExpr {
 | 
			
		||||
	p.exprLevel++
 | 
			
		||||
 | 
			
		||||
	var list []Expr
 | 
			
		||||
	for p.token != token.RParen && p.token != token.EOF {
 | 
			
		||||
	var ellipsis Pos
 | 
			
		||||
	for p.token != token.RParen && p.token != token.EOF && !ellipsis.IsValid() {
 | 
			
		||||
		list = append(list, p.parseExpr())
 | 
			
		||||
 | 
			
		||||
		if p.token == token.Ellipsis {
 | 
			
		||||
			ellipsis = p.pos
 | 
			
		||||
			p.next()
 | 
			
		||||
		}
 | 
			
		||||
		if !p.expectComma(token.RParen, "call argument") {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
@@ -281,10 +285,11 @@ func (p *Parser) parseCall(x Expr) *CallExpr {
 | 
			
		||||
	p.exprLevel--
 | 
			
		||||
	rparen := p.expect(token.RParen)
 | 
			
		||||
	return &CallExpr{
 | 
			
		||||
		Func:   x,
 | 
			
		||||
		LParen: lparen,
 | 
			
		||||
		RParen: rparen,
 | 
			
		||||
		Args:   list,
 | 
			
		||||
		Func:     x,
 | 
			
		||||
		LParen:   lparen,
 | 
			
		||||
		RParen:   rparen,
 | 
			
		||||
		Ellipsis: ellipsis,
 | 
			
		||||
		Args:     list,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/d5/tengo/v2/script.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/d5/tengo/v2/script.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,6 +3,7 @@ package tengo
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/d5/tengo/v2/parser"
 | 
			
		||||
@@ -16,6 +17,7 @@ type Script struct {
 | 
			
		||||
	maxAllocs        int64
 | 
			
		||||
	maxConstObjects  int
 | 
			
		||||
	enableFileImport bool
 | 
			
		||||
	importDir        string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewScript creates a Script instance with an input script.
 | 
			
		||||
@@ -56,6 +58,16 @@ func (s *Script) SetImports(modules *ModuleMap) {
 | 
			
		||||
	s.modules = modules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetImportDir sets the initial import directory for script files.
 | 
			
		||||
func (s *Script) SetImportDir(dir string) error {
 | 
			
		||||
	dir, err := filepath.Abs(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	s.importDir = dir
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetMaxAllocs sets the maximum number of objects allocations during the run
 | 
			
		||||
// time. Compiled script will return ErrObjectAllocLimit error if it
 | 
			
		||||
// exceeds this limit.
 | 
			
		||||
@@ -93,6 +105,7 @@ func (s *Script) Compile() (*Compiled, error) {
 | 
			
		||||
 | 
			
		||||
	c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
 | 
			
		||||
	c.EnableFileImport(s.enableFileImport)
 | 
			
		||||
	c.SetImportDir(s.importDir)
 | 
			
		||||
	if err := c.Compile(file); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										195
									
								
								vendor/github.com/d5/tengo/v2/stdlib/json/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										195
									
								
								vendor/github.com/d5/tengo/v2/stdlib/json/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,20 +1,129 @@
 | 
			
		||||
// A modified version of Go's JSON implementation.
 | 
			
		||||
 | 
			
		||||
// Copyright 2010 The Go Authors. All rights reserved.
 | 
			
		||||
// Copyright 2010, 2020 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/d5/tengo/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// safeSet holds the value true if the ASCII character with the given array
 | 
			
		||||
// position can be represented inside a JSON string without any further
 | 
			
		||||
// escaping.
 | 
			
		||||
//
 | 
			
		||||
// All values are true except for the ASCII control characters (0-31), the
 | 
			
		||||
// double quote ("), and the backslash character ("\").
 | 
			
		||||
var safeSet = [utf8.RuneSelf]bool{
 | 
			
		||||
	' ':      true,
 | 
			
		||||
	'!':      true,
 | 
			
		||||
	'"':      false,
 | 
			
		||||
	'#':      true,
 | 
			
		||||
	'$':      true,
 | 
			
		||||
	'%':      true,
 | 
			
		||||
	'&':      true,
 | 
			
		||||
	'\'':     true,
 | 
			
		||||
	'(':      true,
 | 
			
		||||
	')':      true,
 | 
			
		||||
	'*':      true,
 | 
			
		||||
	'+':      true,
 | 
			
		||||
	',':      true,
 | 
			
		||||
	'-':      true,
 | 
			
		||||
	'.':      true,
 | 
			
		||||
	'/':      true,
 | 
			
		||||
	'0':      true,
 | 
			
		||||
	'1':      true,
 | 
			
		||||
	'2':      true,
 | 
			
		||||
	'3':      true,
 | 
			
		||||
	'4':      true,
 | 
			
		||||
	'5':      true,
 | 
			
		||||
	'6':      true,
 | 
			
		||||
	'7':      true,
 | 
			
		||||
	'8':      true,
 | 
			
		||||
	'9':      true,
 | 
			
		||||
	':':      true,
 | 
			
		||||
	';':      true,
 | 
			
		||||
	'<':      true,
 | 
			
		||||
	'=':      true,
 | 
			
		||||
	'>':      true,
 | 
			
		||||
	'?':      true,
 | 
			
		||||
	'@':      true,
 | 
			
		||||
	'A':      true,
 | 
			
		||||
	'B':      true,
 | 
			
		||||
	'C':      true,
 | 
			
		||||
	'D':      true,
 | 
			
		||||
	'E':      true,
 | 
			
		||||
	'F':      true,
 | 
			
		||||
	'G':      true,
 | 
			
		||||
	'H':      true,
 | 
			
		||||
	'I':      true,
 | 
			
		||||
	'J':      true,
 | 
			
		||||
	'K':      true,
 | 
			
		||||
	'L':      true,
 | 
			
		||||
	'M':      true,
 | 
			
		||||
	'N':      true,
 | 
			
		||||
	'O':      true,
 | 
			
		||||
	'P':      true,
 | 
			
		||||
	'Q':      true,
 | 
			
		||||
	'R':      true,
 | 
			
		||||
	'S':      true,
 | 
			
		||||
	'T':      true,
 | 
			
		||||
	'U':      true,
 | 
			
		||||
	'V':      true,
 | 
			
		||||
	'W':      true,
 | 
			
		||||
	'X':      true,
 | 
			
		||||
	'Y':      true,
 | 
			
		||||
	'Z':      true,
 | 
			
		||||
	'[':      true,
 | 
			
		||||
	'\\':     false,
 | 
			
		||||
	']':      true,
 | 
			
		||||
	'^':      true,
 | 
			
		||||
	'_':      true,
 | 
			
		||||
	'`':      true,
 | 
			
		||||
	'a':      true,
 | 
			
		||||
	'b':      true,
 | 
			
		||||
	'c':      true,
 | 
			
		||||
	'd':      true,
 | 
			
		||||
	'e':      true,
 | 
			
		||||
	'f':      true,
 | 
			
		||||
	'g':      true,
 | 
			
		||||
	'h':      true,
 | 
			
		||||
	'i':      true,
 | 
			
		||||
	'j':      true,
 | 
			
		||||
	'k':      true,
 | 
			
		||||
	'l':      true,
 | 
			
		||||
	'm':      true,
 | 
			
		||||
	'n':      true,
 | 
			
		||||
	'o':      true,
 | 
			
		||||
	'p':      true,
 | 
			
		||||
	'q':      true,
 | 
			
		||||
	'r':      true,
 | 
			
		||||
	's':      true,
 | 
			
		||||
	't':      true,
 | 
			
		||||
	'u':      true,
 | 
			
		||||
	'v':      true,
 | 
			
		||||
	'w':      true,
 | 
			
		||||
	'x':      true,
 | 
			
		||||
	'y':      true,
 | 
			
		||||
	'z':      true,
 | 
			
		||||
	'{':      true,
 | 
			
		||||
	'|':      true,
 | 
			
		||||
	'}':      true,
 | 
			
		||||
	'~':      true,
 | 
			
		||||
	'\u007f': true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var hex = "0123456789abcdef"
 | 
			
		||||
 | 
			
		||||
// Encode returns the JSON encoding of the object.
 | 
			
		||||
func Encode(o tengo.Object) ([]byte, error) {
 | 
			
		||||
	var b []byte
 | 
			
		||||
@@ -53,7 +162,7 @@ func Encode(o tengo.Object) ([]byte, error) {
 | 
			
		||||
		len1 := len(o.Value) - 1
 | 
			
		||||
		idx := 0
 | 
			
		||||
		for key, value := range o.Value {
 | 
			
		||||
			b = strconv.AppendQuote(b, key)
 | 
			
		||||
			b = encodeString(b, key)
 | 
			
		||||
			b = append(b, ':')
 | 
			
		||||
			eb, err := Encode(value)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -71,7 +180,7 @@ func Encode(o tengo.Object) ([]byte, error) {
 | 
			
		||||
		len1 := len(o.Value) - 1
 | 
			
		||||
		idx := 0
 | 
			
		||||
		for key, value := range o.Value {
 | 
			
		||||
			b = strconv.AppendQuote(b, key)
 | 
			
		||||
			b = encodeString(b, key)
 | 
			
		||||
			b = append(b, ':')
 | 
			
		||||
			eb, err := Encode(value)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -130,7 +239,9 @@ func Encode(o tengo.Object) ([]byte, error) {
 | 
			
		||||
	case *tengo.Int:
 | 
			
		||||
		b = strconv.AppendInt(b, o.Value, 10)
 | 
			
		||||
	case *tengo.String:
 | 
			
		||||
		b = strconv.AppendQuote(b, o.Value)
 | 
			
		||||
		// string encoding bug is fixed with newly introduced function
 | 
			
		||||
		// encodeString(). See: https://github.com/d5/tengo/issues/268
 | 
			
		||||
		b = encodeString(b, o.Value)
 | 
			
		||||
	case *tengo.Time:
 | 
			
		||||
		y, err := o.Value.MarshalJSON()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -144,3 +255,79 @@ func Encode(o tengo.Object) ([]byte, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return b, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// encodeString encodes given string as JSON string according to
 | 
			
		||||
// https://www.json.org/img/string.png
 | 
			
		||||
// Implementation is inspired by https://github.com/json-iterator/go
 | 
			
		||||
// See encodeStringSlowPath() for more information.
 | 
			
		||||
func encodeString(b []byte, val string) []byte {
 | 
			
		||||
	valLen := len(val)
 | 
			
		||||
	buf := bytes.NewBuffer(b)
 | 
			
		||||
	buf.WriteByte('"')
 | 
			
		||||
 | 
			
		||||
	// write string, the fast path, without utf8 and escape support
 | 
			
		||||
	i := 0
 | 
			
		||||
	for ; i < valLen; i++ {
 | 
			
		||||
		c := val[i]
 | 
			
		||||
		if c > 31 && c != '"' && c != '\\' {
 | 
			
		||||
			buf.WriteByte(c)
 | 
			
		||||
		} else {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if i == valLen {
 | 
			
		||||
		buf.WriteByte('"')
 | 
			
		||||
		return buf.Bytes()
 | 
			
		||||
	}
 | 
			
		||||
	encodeStringSlowPath(buf, i, val, valLen)
 | 
			
		||||
	buf.WriteByte('"')
 | 
			
		||||
	return buf.Bytes()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// encodeStringSlowPath is ported from Go 1.14.2 encoding/json package.
 | 
			
		||||
// U+2028 U+2029 JSONP security holes can be fixed with addition call to
 | 
			
		||||
// json.html_escape() thus it is removed from the implementation below.
 | 
			
		||||
// Note: Invalid runes are not checked as they are checked in original
 | 
			
		||||
// implementation.
 | 
			
		||||
func encodeStringSlowPath(buf *bytes.Buffer, i int, val string, valLen int) {
 | 
			
		||||
	start := i
 | 
			
		||||
	for i < valLen {
 | 
			
		||||
		if b := val[i]; b < utf8.RuneSelf {
 | 
			
		||||
			if safeSet[b] {
 | 
			
		||||
				i++
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if start < i {
 | 
			
		||||
				buf.WriteString(val[start:i])
 | 
			
		||||
			}
 | 
			
		||||
			buf.WriteByte('\\')
 | 
			
		||||
			switch b {
 | 
			
		||||
			case '\\', '"':
 | 
			
		||||
				buf.WriteByte(b)
 | 
			
		||||
			case '\n':
 | 
			
		||||
				buf.WriteByte('n')
 | 
			
		||||
			case '\r':
 | 
			
		||||
				buf.WriteByte('r')
 | 
			
		||||
			case '\t':
 | 
			
		||||
				buf.WriteByte('t')
 | 
			
		||||
			default:
 | 
			
		||||
				// This encodes bytes < 0x20 except for \t, \n and \r.
 | 
			
		||||
				// If escapeHTML is set, it also escapes <, >, and &
 | 
			
		||||
				// because they can lead to security holes when
 | 
			
		||||
				// user-controlled strings are rendered into JSON
 | 
			
		||||
				// and served to some browsers.
 | 
			
		||||
				buf.WriteString(`u00`)
 | 
			
		||||
				buf.WriteByte(hex[b>>4])
 | 
			
		||||
				buf.WriteByte(hex[b&0xF])
 | 
			
		||||
			}
 | 
			
		||||
			i++
 | 
			
		||||
			start = i
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		i++
 | 
			
		||||
		continue
 | 
			
		||||
	}
 | 
			
		||||
	if start < valLen {
 | 
			
		||||
		buf.WriteString(val[start:])
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/d5/tengo/v2/vm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/d5/tengo/v2/vm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -80,14 +80,14 @@ func (v *VM) Run() (err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		filePos := v.fileSet.Position(
 | 
			
		||||
			v.curFrame.fn.SourcePos(v.ip - 1))
 | 
			
		||||
		err = fmt.Errorf("Runtime Error: %s\n\tat %s",
 | 
			
		||||
			err.Error(), filePos)
 | 
			
		||||
		err = fmt.Errorf("Runtime Error: %w\n\tat %s",
 | 
			
		||||
			err, filePos)
 | 
			
		||||
		for v.framesIndex > 1 {
 | 
			
		||||
			v.framesIndex--
 | 
			
		||||
			v.curFrame = &v.frames[v.framesIndex-1]
 | 
			
		||||
			filePos = v.fileSet.Position(
 | 
			
		||||
				v.curFrame.fn.SourcePos(v.curFrame.ip - 1))
 | 
			
		||||
			err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
 | 
			
		||||
			err = fmt.Errorf("%w\n\tat %s", err, filePos)
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -537,12 +537,36 @@ func (v *VM) run() {
 | 
			
		||||
			}
 | 
			
		||||
		case parser.OpCall:
 | 
			
		||||
			numArgs := int(v.curInsts[v.ip+1])
 | 
			
		||||
			v.ip++
 | 
			
		||||
			spread := int(v.curInsts[v.ip+2])
 | 
			
		||||
			v.ip += 2
 | 
			
		||||
 | 
			
		||||
			value := v.stack[v.sp-1-numArgs]
 | 
			
		||||
			if !value.CanCall() {
 | 
			
		||||
				v.err = fmt.Errorf("not callable: %s", value.TypeName())
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if spread == 1 {
 | 
			
		||||
				v.sp--
 | 
			
		||||
				switch arr := v.stack[v.sp].(type) {
 | 
			
		||||
				case *Array:
 | 
			
		||||
					for _, item := range arr.Value {
 | 
			
		||||
						v.stack[v.sp] = item
 | 
			
		||||
						v.sp++
 | 
			
		||||
					}
 | 
			
		||||
					numArgs += len(arr.Value) - 1
 | 
			
		||||
				case *ImmutableArray:
 | 
			
		||||
					for _, item := range arr.Value {
 | 
			
		||||
						v.stack[v.sp] = item
 | 
			
		||||
						v.sp++
 | 
			
		||||
					}
 | 
			
		||||
					numArgs += len(arr.Value) - 1
 | 
			
		||||
				default:
 | 
			
		||||
					v.err = fmt.Errorf("not an array: %s", arr.TypeName())
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if callee, ok := value.(*CompiledFunction); ok {
 | 
			
		||||
				if callee.VarArgs {
 | 
			
		||||
					// if the closure is variadic,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/dyatlov/go-opengraph/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/dyatlov/go-opengraph/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2015 Vitaly Dyatlov
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										365
									
								
								vendor/github.com/dyatlov/go-opengraph/opengraph/opengraph.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								vendor/github.com/dyatlov/go-opengraph/opengraph/opengraph.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
			
		||||
package opengraph
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/html"
 | 
			
		||||
	"golang.org/x/net/html/atom"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Image defines Open Graph Image type
 | 
			
		||||
type Image struct {
 | 
			
		||||
	URL       string `json:"url"`
 | 
			
		||||
	SecureURL string `json:"secure_url"`
 | 
			
		||||
	Type      string `json:"type"`
 | 
			
		||||
	Width     uint64 `json:"width"`
 | 
			
		||||
	Height    uint64 `json:"height"`
 | 
			
		||||
	draft     bool    `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Video defines Open Graph Video type
 | 
			
		||||
type Video struct {
 | 
			
		||||
	URL       string `json:"url"`
 | 
			
		||||
	SecureURL string `json:"secure_url"`
 | 
			
		||||
	Type      string `json:"type"`
 | 
			
		||||
	Width     uint64 `json:"width"`
 | 
			
		||||
	Height    uint64 `json:"height"`
 | 
			
		||||
	draft     bool    `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Audio defines Open Graph Audio Type
 | 
			
		||||
type Audio struct {
 | 
			
		||||
	URL       string `json:"url"`
 | 
			
		||||
	SecureURL string `json:"secure_url"`
 | 
			
		||||
	Type      string `json:"type"`
 | 
			
		||||
	draft     bool    `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Article contain Open Graph Article structure
 | 
			
		||||
type Article struct {
 | 
			
		||||
	PublishedTime  *time.Time `json:"published_time"`
 | 
			
		||||
	ModifiedTime   *time.Time `json:"modified_time"`
 | 
			
		||||
	ExpirationTime *time.Time `json:"expiration_time"`
 | 
			
		||||
	Section        string     `json:"section"`
 | 
			
		||||
	Tags           []string   `json:"tags"`
 | 
			
		||||
	Authors        []*Profile `json:"authors"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Profile contains Open Graph Profile structure
 | 
			
		||||
type Profile struct {
 | 
			
		||||
	FirstName string `json:"first_name"`
 | 
			
		||||
	LastName  string `json:"last_name"`
 | 
			
		||||
	Username  string `json:"username"`
 | 
			
		||||
	Gender    string `json:"gender"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Book contains Open Graph Book structure
 | 
			
		||||
type Book struct {
 | 
			
		||||
	ISBN        string     `json:"isbn"`
 | 
			
		||||
	ReleaseDate *time.Time `json:"release_date"`
 | 
			
		||||
	Tags        []string   `json:"tags"`
 | 
			
		||||
	Authors     []*Profile `json:"authors"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenGraph contains facebook og data
 | 
			
		||||
type OpenGraph struct {
 | 
			
		||||
	isArticle        bool
 | 
			
		||||
	isBook           bool
 | 
			
		||||
	isProfile        bool
 | 
			
		||||
	Type             string   `json:"type"`
 | 
			
		||||
	URL              string   `json:"url"`
 | 
			
		||||
	Title            string   `json:"title"`
 | 
			
		||||
	Description      string   `json:"description"`
 | 
			
		||||
	Determiner       string   `json:"determiner"`
 | 
			
		||||
	SiteName         string   `json:"site_name"`
 | 
			
		||||
	Locale           string   `json:"locale"`
 | 
			
		||||
	LocalesAlternate []string `json:"locales_alternate"`
 | 
			
		||||
	Images           []*Image `json:"images"`
 | 
			
		||||
	Audios           []*Audio `json:"audios"`
 | 
			
		||||
	Videos           []*Video `json:"videos"`
 | 
			
		||||
	Article          *Article `json:"article,omitempty"`
 | 
			
		||||
	Book             *Book    `json:"book,omitempty"`
 | 
			
		||||
	Profile          *Profile `json:"profile,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOpenGraph returns new instance of Open Graph structure
 | 
			
		||||
func NewOpenGraph() *OpenGraph {
 | 
			
		||||
	return &OpenGraph{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToJSON a simple wrapper around json.Marshal
 | 
			
		||||
func (og *OpenGraph) ToJSON() ([]byte, error) {
 | 
			
		||||
	return json.Marshal(og)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String return json representation of structure, or error string
 | 
			
		||||
func (og *OpenGraph) String() string {
 | 
			
		||||
	data, err := og.ToJSON()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err.Error()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(data[:])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessHTML parses given html from Reader interface and fills up OpenGraph structure
 | 
			
		||||
func (og *OpenGraph) ProcessHTML(buffer io.Reader) error {
 | 
			
		||||
	z := html.NewTokenizer(buffer)
 | 
			
		||||
	for {
 | 
			
		||||
		tt := z.Next()
 | 
			
		||||
		switch tt {
 | 
			
		||||
		case html.ErrorToken:
 | 
			
		||||
			if z.Err() == io.EOF {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return z.Err()
 | 
			
		||||
		case html.StartTagToken, html.SelfClosingTagToken, html.EndTagToken:
 | 
			
		||||
			name, hasAttr := z.TagName()
 | 
			
		||||
			if atom.Lookup(name) == atom.Body {
 | 
			
		||||
				return nil // OpenGraph is only in head, so we don't need body
 | 
			
		||||
			}
 | 
			
		||||
			if atom.Lookup(name) != atom.Meta || !hasAttr {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			m := make(map[string]string)
 | 
			
		||||
			var key, val []byte
 | 
			
		||||
			for hasAttr {
 | 
			
		||||
				key, val, hasAttr = z.TagAttr()
 | 
			
		||||
				m[atom.String(key)] = string(val)
 | 
			
		||||
			}
 | 
			
		||||
			og.ProcessMeta(m)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (og *OpenGraph) ensureHasVideo() {
 | 
			
		||||
	if len(og.Videos) > 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	og.Videos = append(og.Videos, &Video{draft: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (og *OpenGraph) ensureHasImage() {
 | 
			
		||||
	if len(og.Images) > 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	og.Images = append(og.Images, &Image{draft: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (og *OpenGraph) ensureHasAudio() {
 | 
			
		||||
	if len(og.Audios) > 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	og.Audios = append(og.Audios, &Audio{draft: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessMeta processes meta attributes and adds them to Open Graph structure if they are suitable for that
 | 
			
		||||
func (og *OpenGraph) ProcessMeta(metaAttrs map[string]string) {
 | 
			
		||||
	switch metaAttrs["property"] {
 | 
			
		||||
	case "og:description":
 | 
			
		||||
		og.Description = metaAttrs["content"]
 | 
			
		||||
	case "og:type":
 | 
			
		||||
		og.Type = metaAttrs["content"]
 | 
			
		||||
		switch og.Type {
 | 
			
		||||
		case "article":
 | 
			
		||||
			og.isArticle = true
 | 
			
		||||
		case "book":
 | 
			
		||||
			og.isBook = true
 | 
			
		||||
		case "profile":
 | 
			
		||||
			og.isProfile = true
 | 
			
		||||
		}
 | 
			
		||||
	case "og:title":
 | 
			
		||||
		og.Title = metaAttrs["content"]
 | 
			
		||||
	case "og:url":
 | 
			
		||||
		og.URL = metaAttrs["content"]
 | 
			
		||||
	case "og:determiner":
 | 
			
		||||
		og.Determiner = metaAttrs["content"]
 | 
			
		||||
	case "og:site_name":
 | 
			
		||||
		og.SiteName = metaAttrs["content"]
 | 
			
		||||
	case "og:locale":
 | 
			
		||||
		og.Locale = metaAttrs["content"]
 | 
			
		||||
	case "og:locale:alternate":
 | 
			
		||||
		og.LocalesAlternate = append(og.LocalesAlternate, metaAttrs["content"])
 | 
			
		||||
	case "og:audio":
 | 
			
		||||
		if len(og.Audios)>0 && og.Audios[len(og.Audios)-1].draft {
 | 
			
		||||
			og.Audios[len(og.Audios)-1].URL = metaAttrs["content"]
 | 
			
		||||
			og.Audios[len(og.Audios)-1].draft = false
 | 
			
		||||
		} else {
 | 
			
		||||
			og.Audios = append(og.Audios, &Audio{URL: metaAttrs["content"]})
 | 
			
		||||
		}
 | 
			
		||||
	case "og:audio:secure_url":
 | 
			
		||||
		og.ensureHasAudio()
 | 
			
		||||
		og.Audios[len(og.Audios)-1].SecureURL = metaAttrs["content"]
 | 
			
		||||
	case "og:audio:type":
 | 
			
		||||
		og.ensureHasAudio()
 | 
			
		||||
		og.Audios[len(og.Audios)-1].Type = metaAttrs["content"]
 | 
			
		||||
	case "og:image":
 | 
			
		||||
		if len(og.Images)>0 && og.Images[len(og.Images)-1].draft {
 | 
			
		||||
			og.Images[len(og.Images)-1].URL = metaAttrs["content"]
 | 
			
		||||
			og.Images[len(og.Images)-1].draft = false
 | 
			
		||||
		} else {
 | 
			
		||||
			og.Images = append(og.Images, &Image{URL: metaAttrs["content"]})
 | 
			
		||||
		}
 | 
			
		||||
	case "og:image:url":
 | 
			
		||||
		og.ensureHasImage()
 | 
			
		||||
		og.Images[len(og.Images)-1].URL = metaAttrs["content"]
 | 
			
		||||
	case "og:image:secure_url":
 | 
			
		||||
		og.ensureHasImage()
 | 
			
		||||
		og.Images[len(og.Images)-1].SecureURL = metaAttrs["content"]
 | 
			
		||||
	case "og:image:type":
 | 
			
		||||
		og.ensureHasImage()
 | 
			
		||||
		og.Images[len(og.Images)-1].Type = metaAttrs["content"]
 | 
			
		||||
	case "og:image:width":
 | 
			
		||||
		w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.ensureHasImage()
 | 
			
		||||
			og.Images[len(og.Images)-1].Width = w
 | 
			
		||||
		}
 | 
			
		||||
	case "og:image:height":
 | 
			
		||||
		h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.ensureHasImage()
 | 
			
		||||
			og.Images[len(og.Images)-1].Height = h
 | 
			
		||||
		}
 | 
			
		||||
	case "og:video":
 | 
			
		||||
		if len(og.Videos)>0 && og.Videos[len(og.Videos)-1].draft {
 | 
			
		||||
			og.Videos[len(og.Videos)-1].URL = metaAttrs["content"]
 | 
			
		||||
			og.Videos[len(og.Videos)-1].draft = false
 | 
			
		||||
		} else {
 | 
			
		||||
			og.Videos = append(og.Videos, &Video{URL: metaAttrs["content"]})
 | 
			
		||||
		}
 | 
			
		||||
	case "og:video:url":
 | 
			
		||||
		og.ensureHasVideo()
 | 
			
		||||
		og.Videos[len(og.Videos)-1].URL = metaAttrs["content"]
 | 
			
		||||
	case "og:video:secure_url":
 | 
			
		||||
		og.ensureHasVideo()
 | 
			
		||||
		og.Videos[len(og.Videos)-1].SecureURL = metaAttrs["content"]
 | 
			
		||||
	case "og:video:type":
 | 
			
		||||
		og.ensureHasVideo()
 | 
			
		||||
		og.Videos[len(og.Videos)-1].Type = metaAttrs["content"]
 | 
			
		||||
	case "og:video:width":
 | 
			
		||||
		w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.ensureHasVideo()
 | 
			
		||||
			og.Videos[len(og.Videos)-1].Width = w
 | 
			
		||||
		}
 | 
			
		||||
	case "og:video:height":
 | 
			
		||||
		h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.ensureHasVideo()
 | 
			
		||||
			og.Videos[len(og.Videos)-1].Height = h
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		if og.isArticle {
 | 
			
		||||
			og.processArticleMeta(metaAttrs)
 | 
			
		||||
		} else if og.isBook {
 | 
			
		||||
			og.processBookMeta(metaAttrs)
 | 
			
		||||
		} else if og.isProfile {
 | 
			
		||||
			og.processProfileMeta(metaAttrs)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (og *OpenGraph) processArticleMeta(metaAttrs map[string]string) {
 | 
			
		||||
	if og.Article == nil {
 | 
			
		||||
		og.Article = &Article{}
 | 
			
		||||
	}
 | 
			
		||||
	switch metaAttrs["property"] {
 | 
			
		||||
	case "article:published_time":
 | 
			
		||||
		t, err := time.Parse(time.RFC3339, metaAttrs["content"])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.Article.PublishedTime = &t
 | 
			
		||||
		}
 | 
			
		||||
	case "article:modified_time":
 | 
			
		||||
		t, err := time.Parse(time.RFC3339, metaAttrs["content"])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.Article.ModifiedTime = &t
 | 
			
		||||
		}
 | 
			
		||||
	case "article:expiration_time":
 | 
			
		||||
		t, err := time.Parse(time.RFC3339, metaAttrs["content"])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.Article.ExpirationTime = &t
 | 
			
		||||
		}
 | 
			
		||||
	case "article:section":
 | 
			
		||||
		og.Article.Section = metaAttrs["content"]
 | 
			
		||||
	case "article:tag":
 | 
			
		||||
		og.Article.Tags = append(og.Article.Tags, metaAttrs["content"])
 | 
			
		||||
	case "article:author:first_name":
 | 
			
		||||
		if len(og.Article.Authors) == 0 {
 | 
			
		||||
			og.Article.Authors = append(og.Article.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Article.Authors[len(og.Article.Authors)-1].FirstName = metaAttrs["content"]
 | 
			
		||||
	case "article:author:last_name":
 | 
			
		||||
		if len(og.Article.Authors) == 0 {
 | 
			
		||||
			og.Article.Authors = append(og.Article.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Article.Authors[len(og.Article.Authors)-1].LastName = metaAttrs["content"]
 | 
			
		||||
	case "article:author:username":
 | 
			
		||||
		if len(og.Article.Authors) == 0 {
 | 
			
		||||
			og.Article.Authors = append(og.Article.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Article.Authors[len(og.Article.Authors)-1].Username = metaAttrs["content"]
 | 
			
		||||
	case "article:author:gender":
 | 
			
		||||
		if len(og.Article.Authors) == 0 {
 | 
			
		||||
			og.Article.Authors = append(og.Article.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Article.Authors[len(og.Article.Authors)-1].Gender = metaAttrs["content"]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (og *OpenGraph) processBookMeta(metaAttrs map[string]string) {
 | 
			
		||||
	if og.Book == nil {
 | 
			
		||||
		og.Book = &Book{}
 | 
			
		||||
	}
 | 
			
		||||
	switch metaAttrs["property"] {
 | 
			
		||||
	case "book:release_date":
 | 
			
		||||
		t, err := time.Parse(time.RFC3339, metaAttrs["content"])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			og.Book.ReleaseDate = &t
 | 
			
		||||
		}
 | 
			
		||||
	case "book:isbn":
 | 
			
		||||
		og.Book.ISBN = metaAttrs["content"]
 | 
			
		||||
	case "book:tag":
 | 
			
		||||
		og.Book.Tags = append(og.Book.Tags, metaAttrs["content"])
 | 
			
		||||
	case "book:author:first_name":
 | 
			
		||||
		if len(og.Book.Authors) == 0 {
 | 
			
		||||
			og.Book.Authors = append(og.Book.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Book.Authors[len(og.Book.Authors)-1].FirstName = metaAttrs["content"]
 | 
			
		||||
	case "book:author:last_name":
 | 
			
		||||
		if len(og.Book.Authors) == 0 {
 | 
			
		||||
			og.Book.Authors = append(og.Book.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Book.Authors[len(og.Book.Authors)-1].LastName = metaAttrs["content"]
 | 
			
		||||
	case "book:author:username":
 | 
			
		||||
		if len(og.Book.Authors) == 0 {
 | 
			
		||||
			og.Book.Authors = append(og.Book.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Book.Authors[len(og.Book.Authors)-1].Username = metaAttrs["content"]
 | 
			
		||||
	case "book:author:gender":
 | 
			
		||||
		if len(og.Book.Authors) == 0 {
 | 
			
		||||
			og.Book.Authors = append(og.Book.Authors, &Profile{})
 | 
			
		||||
		}
 | 
			
		||||
		og.Book.Authors[len(og.Book.Authors)-1].Gender = metaAttrs["content"]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (og *OpenGraph) processProfileMeta(metaAttrs map[string]string) {
 | 
			
		||||
	if og.Profile == nil {
 | 
			
		||||
		og.Profile = &Profile{}
 | 
			
		||||
	}
 | 
			
		||||
	switch metaAttrs["property"] {
 | 
			
		||||
	case "profile:first_name":
 | 
			
		||||
		og.Profile.FirstName = metaAttrs["content"]
 | 
			
		||||
	case "profile:last_name":
 | 
			
		||||
		og.Profile.LastName = metaAttrs["content"]
 | 
			
		||||
	case "profile:username":
 | 
			
		||||
		og.Profile.Username = metaAttrs["content"]
 | 
			
		||||
	case "profile:gender":
 | 
			
		||||
		og.Profile.Gender = metaAttrs["content"]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/francoispqt/gojay/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/francoispqt/gojay/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
vendor
 | 
			
		||||
*.out
 | 
			
		||||
*.log
 | 
			
		||||
*.test
 | 
			
		||||
.vscode
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/github.com/francoispqt/gojay/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/francoispqt/gojay/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
  - "1.10.x"
 | 
			
		||||
  - "1.11.x"
 | 
			
		||||
  - "1.12.x"
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - go get github.com/golang/dep/cmd/dep github.com/stretchr/testify
 | 
			
		||||
  - dep ensure -v -vendor-only
 | 
			
		||||
  - go test ./gojay/codegen/test/... -race
 | 
			
		||||
  - go test -race -coverprofile=coverage.txt -covermode=atomic
 | 
			
		||||
 | 
			
		||||
after_success:
 | 
			
		||||
  - bash <(curl -s https://codecov.io/bash)
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user