forked from lug/matterbridge
		
	Compare commits
	
		
			61 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | 
							
								
								
									
										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@v1
 | 
			
		||||
        with:
 | 
			
		||||
          version: v1.27
 | 
			
		||||
          args: "-v --new-from-rev HEAD~1"
 | 
			
		||||
  test-build-upload:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        go-version: [1.13.x, 1.14.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.14')
 | 
			
		||||
      uses: actions/upload-artifact@v2
 | 
			
		||||
      with:
 | 
			
		||||
        name: matterbridge-linux-64bit
 | 
			
		||||
        path: output/lin
 | 
			
		||||
    - name: Upload windows 64-bit
 | 
			
		||||
      if: startsWith(matrix.go-version,'1.14')
 | 
			
		||||
      uses: actions/upload-artifact@v2
 | 
			
		||||
      with:
 | 
			
		||||
        name: matterbridge-windows-64bit
 | 
			
		||||
        path: output/win
 | 
			
		||||
    - name: Upload darwin 64-bit
 | 
			
		||||
      if: startsWith(matrix.go-version,'1.14')
 | 
			
		||||
      uses: actions/upload-artifact@v2
 | 
			
		||||
      with:
 | 
			
		||||
        name: matterbridge-darwin-64bit
 | 
			
		||||
        path: output/mac
 | 
			
		||||
@@ -176,6 +176,10 @@ linters:
 | 
			
		||||
    - prealloc
 | 
			
		||||
    - wsl
 | 
			
		||||
    - gomnd
 | 
			
		||||
    - godox
 | 
			
		||||
    - goerr113
 | 
			
		||||
    - testpackage
 | 
			
		||||
    - godot
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# rules to deal with reported isues
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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="
 | 
			
		||||
@@ -10,4 +10,7 @@ RUN apk update && apk add go git gcc musl-dev \
 | 
			
		||||
FROM alpine:edge
 | 
			
		||||
RUN apk --no-cache add ca-certificates mailcap
 | 
			
		||||
COPY --from=builder /bin/matterbridge /bin/matterbridge
 | 
			
		||||
ENTRYPOINT ["/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"]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								README.md
									
									
									
									
									
								
							@@ -9,27 +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] |
 | 
			
		||||
[MSTeams][mb-msteams] |
 | 
			
		||||
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 />
 | 
			
		||||
 | 
			
		||||
@@ -87,30 +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)
 | 
			
		||||
- [Microsoft Teams](https://teams.microsoft.com)
 | 
			
		||||
- [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
 | 
			
		||||
 | 
			
		||||
@@ -130,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
 | 
			
		||||
 | 
			
		||||
@@ -151,14 +152,15 @@ See https://github.com/42wim/matterbridge/wiki
 | 
			
		||||
 | 
			
		||||
### Binaries
 | 
			
		||||
 | 
			
		||||
- Latest stable release [v1.17.2](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.0](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
 | 
			
		||||
 | 
			
		||||
@@ -306,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
 | 
			
		||||
 | 
			
		||||
@@ -324,32 +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
 | 
			
		||||
- msgraph.go - https://github.com/yaegashi/msgraph.go
 | 
			
		||||
- 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
 | 
			
		||||
[mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7
 | 
			
		||||
 
 | 
			
		||||
@@ -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,13 +85,16 @@ 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
 | 
			
		||||
@@ -134,6 +138,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 +221,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 +249,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 +315,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 +380,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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,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 {
 | 
			
		||||
@@ -41,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"))
 | 
			
		||||
@@ -188,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)
 | 
			
		||||
@@ -224,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 {
 | 
			
		||||
@@ -273,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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -283,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
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -217,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",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -188,8 +188,9 @@ func replaceEmotes(text string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
@@ -208,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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -124,6 +124,14 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
 | 
			
		||||
		return resp.EventID, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b.GetBool("HTMLDisable") {
 | 
			
		||||
		resp, err := b.mc.SendText(channel, msg.Username+msg.Text)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return resp.EventID, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	username := html.EscapeString(msg.Username)
 | 
			
		||||
	// check if we have a </tag>. if we have, we don't escape HTML. #696
 | 
			
		||||
	if b.htmlTag.MatchString(msg.Username) {
 | 
			
		||||
@@ -372,6 +380,8 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge"
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
 | 
			
		||||
	"github.com/mattn/godown"
 | 
			
		||||
	msgraph "github.com/yaegashi/msgraph.go/beta"
 | 
			
		||||
@@ -71,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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -120,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
 | 
			
		||||
@@ -138,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]
 | 
			
		||||
@@ -150,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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ 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 {
 | 
			
		||||
@@ -137,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.
 | 
			
		||||
@@ -155,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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -312,6 +322,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")
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ const (
 | 
			
		||||
	unknownUser = "unknown"
 | 
			
		||||
	HTMLFormat  = "HTML"
 | 
			
		||||
	HTMLNick    = "htmlnick"
 | 
			
		||||
	MarkdownV2  = "MarkdownV2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Btelegram struct {
 | 
			
		||||
@@ -126,6 +127,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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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) {
 | 
			
		||||
@@ -230,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
 | 
			
		||||
@@ -239,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,
 | 
			
		||||
@@ -255,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.
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,3 +1,81 @@
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -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/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
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,33 +5,33 @@ 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.1-0.20200408093540-2f227c53b44f
 | 
			
		||||
	github.com/d5/tengo/v2 v2.1.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/gorilla/websocket v1.4.2
 | 
			
		||||
	github.com/hashicorp/golang-lru v0.5.4
 | 
			
		||||
	github.com/hpcloud/tail v1.0.0 // indirect
 | 
			
		||||
	github.com/jpillora/backoff v1.0.0
 | 
			
		||||
	github.com/keybase/go-keybase-chat-bot v0.0.0-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-20200329150250-5812999b292b
 | 
			
		||||
	github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
 | 
			
		||||
	github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
 | 
			
		||||
	github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
 | 
			
		||||
	github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
 | 
			
		||||
	github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
 | 
			
		||||
	github.com/mattermost/mattermost-server v5.5.0+incompatible
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.7 // indirect
 | 
			
		||||
	github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51
 | 
			
		||||
	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
 | 
			
		||||
@@ -44,21 +44,20 @@ require (
 | 
			
		||||
	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
 | 
			
		||||
	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/yaegashi/msgraph.go v0.1.2
 | 
			
		||||
	github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447
 | 
			
		||||
	golang.org/x/image v0.0.0-20191214001246-9130b4cfad52
 | 
			
		||||
	github.com/yaegashi/msgraph.go v0.1.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.1
 | 
			
		||||
	gopkg.in/fsnotify.v1 v1.4.7 // indirect
 | 
			
		||||
	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
 | 
			
		||||
	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43
 | 
			
		||||
 | 
			
		||||
go 1.13
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										324
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										324
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,58 +1,74 @@
 | 
			
		||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 | 
			
		||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 | 
			
		||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 | 
			
		||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 | 
			
		||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 | 
			
		||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 | 
			
		||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 | 
			
		||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
 | 
			
		||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 | 
			
		||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 | 
			
		||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
			
		||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg=
 | 
			
		||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y=
 | 
			
		||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
 | 
			
		||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
 | 
			
		||||
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 | 
			
		||||
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ=
 | 
			
		||||
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
 | 
			
		||||
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
 | 
			
		||||
github.com/Rhymen/go-whatsapp v0.1.1-0.20200408093540-2f227c53b44f h1:uclEol7RbpElhXXmwu38PDeGcgMXNU2vh5DWwzlg7xI=
 | 
			
		||||
github.com/Rhymen/go-whatsapp v0.1.1-0.20200408093540-2f227c53b44f/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
 | 
			
		||||
github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 h1:kb1zvD+xd+XbPUdQ0lMxnRaQ76N5C9vMAClLi8Dyw1Y=
 | 
			
		||||
github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
 | 
			
		||||
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
 | 
			
		||||
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
 | 
			
		||||
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
 | 
			
		||||
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
 | 
			
		||||
github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
 | 
			
		||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
 | 
			
		||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 | 
			
		||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 | 
			
		||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 | 
			
		||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 | 
			
		||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/d5/tengo/v2 v2.1.2 h1:JR5O6qJW2GW9lpv/MfEqK16a/Wpp2y8I0JZZ5fqNOL0=
 | 
			
		||||
github.com/d5/tengo/v2 v2.1.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
 | 
			
		||||
github.com/d5/tengo/v2 v2.6.0 h1:D0cJtpiBzaLJ/Smv6nnUc/LIfO46oKwDx85NZtIRNRI=
 | 
			
		||||
github.com/d5/tengo/v2 v2.6.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX87a2zVnnbPPickIM9Gf9OIhsIgWQ=
 | 
			
		||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
			
		||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
 | 
			
		||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
 | 
			
		||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
 | 
			
		||||
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81 h1:FdZThbRF0R+2qgyBl3KCVNWWBmKm68E+stT3rnQ02Ww=
 | 
			
		||||
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81/go.mod h1:lDm2E64X4OjFdBUA4hlN4mEvbSitvhJdKw7rsA8KHgI=
 | 
			
		||||
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
 | 
			
		||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
@@ -60,34 +76,64 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
 | 
			
		||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 h1:wJkj+x9gPYlDyM34C6r3SXPs270coWeh85wu1CsusDo=
 | 
			
		||||
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
 | 
			
		||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 | 
			
		||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725 h1:X6sZdr+t2E2jwajTy/FfXbmAKPFTYxEq9hiFgzMiuPQ=
 | 
			
		||||
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
 | 
			
		||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to=
 | 
			
		||||
github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/gops v0.3.10 h1:M2XZYgfUW+P7AOCLiu4CRb0rQfwnslLyB4B9Mp0vXmE=
 | 
			
		||||
github.com/google/gops v0.3.10/go.mod h1:38bMPVKFh+1X106CPpbLAWtZIR1+xwgzT9gew0kn6w4=
 | 
			
		||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
			
		||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
			
		||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
 | 
			
		||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
 | 
			
		||||
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
 | 
			
		||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
 | 
			
		||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
			
		||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 | 
			
		||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 | 
			
		||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 | 
			
		||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 | 
			
		||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 | 
			
		||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 | 
			
		||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 | 
			
		||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 | 
			
		||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 | 
			
		||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
 | 
			
		||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 | 
			
		||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 | 
			
		||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 | 
			
		||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 | 
			
		||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 | 
			
		||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 | 
			
		||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 | 
			
		||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 | 
			
		||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
@@ -95,69 +141,82 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
 | 
			
		||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 | 
			
		||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
 | 
			
		||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
 | 
			
		||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
 | 
			
		||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e h1:KbPTfR/PYuau1IzKoE4lnd8yby5I2pBj+VR6fSVbYU8=
 | 
			
		||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e/go.mod h1:vNc28YFzigVJod0j5EbuTtRIe7swx8vodh2yA4jZ2s8=
 | 
			
		||||
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999 h1:2d+FLQbz4xRTi36DO1qYNUwfORax9XcQ0jhbO81Vago=
 | 
			
		||||
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
 | 
			
		||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da h1:LK+8uBG3kNikj664cjFt88RBmuGmonxkXv2rUVfbqz4=
 | 
			
		||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da/go.mod h1:xJA+X9ZVyT/irGldcb7q1XnJBq5F9s5H9h2L44Y+poY=
 | 
			
		||||
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
 | 
			
		||||
github.com/labstack/echo/v4 v4.1.13 h1:JYgKq6NQQSaKbQcsOadAKX1kUVLCUzLGwu8sxN5tC34=
 | 
			
		||||
github.com/labstack/echo/v4 v4.1.13/go.mod h1:3WZNypykZ3tnqpF2Qb4fPg27XDunFqgP3HGDmCMgv7U=
 | 
			
		||||
github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o=
 | 
			
		||||
github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
 | 
			
		||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
 | 
			
		||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
 | 
			
		||||
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 h1:BS9tqL0OCiOGuy/CYYk2gc33fxqaqh5/rhqMKu4tcYA=
 | 
			
		||||
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I=
 | 
			
		||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 | 
			
		||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs=
 | 
			
		||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
 | 
			
		||||
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw=
 | 
			
		||||
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y=
 | 
			
		||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 h1:B9HaistmV+MD8/33BXmZe1zPIn+RImAFVXNNSOrwU2E=
 | 
			
		||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
 | 
			
		||||
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d h1:NBckP4nw7qVspbt7cOZYsrOrEbq7tATdMjSjc1hW63A=
 | 
			
		||||
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d/go.mod h1:411nZYv0UMMrtppR5glXop1foboJiFAowy+42U+Ahvw=
 | 
			
		||||
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
 | 
			
		||||
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4=
 | 
			
		||||
github.com/matterbridge/go-xmpp v0.0.0-20200329150250-5812999b292b h1:ZYI2HCj9zPzI4Si1ouSOi/ImA2xSQLUCJPQsLWr8FE0=
 | 
			
		||||
github.com/matterbridge/go-xmpp v0.0.0-20200329150250-5812999b292b/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
 | 
			
		||||
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 h1:kWkP1lXpkvtoNL08jkP3XQH/zvDOEXJpdCJd/DlIvMw=
 | 
			
		||||
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
 | 
			
		||||
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0=
 | 
			
		||||
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
 | 
			
		||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho=
 | 
			
		||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
 | 
			
		||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
 | 
			
		||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
 | 
			
		||||
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba h1:XleOY4IjAEIcxAh+IFwT5JT5Ze3RHiYz6m+4ZfZ0rc0=
 | 
			
		||||
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
 | 
			
		||||
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
 | 
			
		||||
github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y=
 | 
			
		||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 | 
			
		||||
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51 h1:MpI7hy3MiCnrggmZI/s8LaPbLVOOWpzDbjA4F+XaXaM=
 | 
			
		||||
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51/go.mod h1:s3KUdOIXJ+jaGM++XHiXA6gikdleaWVATCcQGD4h734=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 | 
			
		||||
github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561 h1:0YGo77enc6tJpXQxUeQWs9bPIQPTH1lbOmc5tgRuq4o=
 | 
			
		||||
github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561/go.mod h1:/ivCKurgV/bx6yqtP/Jtc2Xmrv3beCYBvlfAUl4X5g4=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
			
		||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 | 
			
		||||
github.com/missdeer/golib v1.0.3 h1:+kz/tn1lXlPS8i+gjHHVAZC8YcgrmfiMTqELyvOwI4g=
 | 
			
		||||
github.com/missdeer/golib v1.0.3/go.mod h1:Cys1ITPPZxIk2eTcQcKT3jDsBdhICAfrrw+ki/eRXxA=
 | 
			
		||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 | 
			
		||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 | 
			
		||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 | 
			
		||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/monaco-io/request v1.0.3 h1:FsiIwXCCbHEyWx9A7lgg6JBTMHhHlEEsADsgAOvZ9HA=
 | 
			
		||||
github.com/monaco-io/request v1.0.3/go.mod h1:EmggwHktBsbJmCgwZXqy7o0H1NNsAstQBWZrFVd3xtQ=
 | 
			
		||||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o=
 | 
			
		||||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
 | 
			
		||||
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9DtP8gnTmNtMxbPyhYltfxsITel8g=
 | 
			
		||||
@@ -167,13 +226,12 @@ github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 h1:mp6tU1r0xLostUGL
 | 
			
		||||
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9/go.mod h1:A5SRAcpTemjGgIuBq6Kic2yHcoeUFWUinOAlMP/i9xo=
 | 
			
		||||
github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I=
 | 
			
		||||
github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
 | 
			
		||||
github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA=
 | 
			
		||||
github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
 | 
			
		||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
 | 
			
		||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 | 
			
		||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 | 
			
		||||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw=
 | 
			
		||||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4=
 | 
			
		||||
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcUhvI17yO9SlpAdDIJGkhDEgO8A=
 | 
			
		||||
@@ -185,6 +243,7 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
@@ -199,25 +258,27 @@ github.com/rickb777/date v1.12.4/go.mod h1:xP0eo/I5qmUt97yRGClHZfyLZ3ikMw6v6SU5M
 | 
			
		||||
github.com/rickb777/plural v1.2.0 h1:5tvEc7UBCZ7l8h/2UeybSkt/uu1DQsZFOFdNevmUhlE=
 | 
			
		||||
github.com/rickb777/plural v1.2.0/go.mod h1:UdpyWFCGbo3mvK3f/PfZOAOrkjzJlYN/sD46XNWJ+Es=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
 | 
			
		||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 | 
			
		||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 | 
			
		||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 | 
			
		||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
 | 
			
		||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
 | 
			
		||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 | 
			
		||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
 | 
			
		||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
 | 
			
		||||
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98 h1:sN07ff+PSRsUNhpSod4uGKAQ+Nc0FXsBPG9FmYMNg4w=
 | 
			
		||||
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98/go.mod h1:xkTgfD+WP+KR4HuG76oal25BBEeu5kJyi2EOsgiu/4Q=
 | 
			
		||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 | 
			
		||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
 | 
			
		||||
github.com/shirou/gopsutil v2.20.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
 | 
			
		||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 | 
			
		||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
 | 
			
		||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
 | 
			
		||||
github.com/slack-go/slack v0.6.3 h1:qU037g8gQ71EuH6S9zYKnvYrEUj0fLFH4HFekFqBoRU=
 | 
			
		||||
github.com/slack-go/slack v0.6.3/go.mod h1:HE4RwNe7YpOg/F0vqo5PwXH3Hki31TplTvKRW9dGGaw=
 | 
			
		||||
github.com/slack-go/slack v0.6.5 h1:IkDKtJ2IROJNoe3d6mW870/NRKvq2fhLB/Q5XmzWk00=
 | 
			
		||||
github.com/slack-go/slack v0.6.5/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 | 
			
		||||
@@ -232,34 +293,40 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
 | 
			
		||||
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
 | 
			
		||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
 | 
			
		||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 | 
			
		||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
			
		||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 | 
			
		||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 | 
			
		||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
 | 
			
		||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
			
		||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
			
		||||
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
 | 
			
		||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
			
		||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
 | 
			
		||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
 | 
			
		||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
 | 
			
		||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
 | 
			
		||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 | 
			
		||||
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
 | 
			
		||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
github.com/yaegashi/msgraph.go v0.1.2 h1:83uVRQaj8YBsVqOUGj0WRwzxdgGF69jRpg5IQYaTvoY=
 | 
			
		||||
github.com/yaegashi/msgraph.go v0.1.2/go.mod h1:Lp39e9oo596G5FcmMKI0cXR3mg/QikSdabgZdbMqbAM=
 | 
			
		||||
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo=
 | 
			
		||||
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
 | 
			
		||||
github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg=
 | 
			
		||||
github.com/yaegashi/msgraph.go v0.1.3 h1:xeknrGbPGqUVvjjtXGuxvesHLNJ6jEfiOtqJToZe0qw=
 | 
			
		||||
github.com/yaegashi/msgraph.go v0.1.3/go.mod h1:dpty8G9hMEC1xBQeXp6Z2hCXKalqczk2BLvK9me/TUU=
 | 
			
		||||
github.com/yaegashi/wtz.go v0.0.2/go.mod h1:nOLA5QXsmdkRxBkP5tljhua13ADHCKirLBrzPf4PEJc=
 | 
			
		||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
			
		||||
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 h1:UQwvu7FjUEdVYofx0U6bsc5odNE7wa5TSA0fl559GcA=
 | 
			
		||||
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
			
		||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
			
		||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
 | 
			
		||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 | 
			
		||||
@@ -268,67 +335,149 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
 | 
			
		||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
			
		||||
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0=
 | 
			
		||||
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 | 
			
		||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
			
		||||
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
 | 
			
		||||
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 | 
			
		||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 | 
			
		||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
			
		||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
 | 
			
		||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
 | 
			
		||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20171017063910-8dbc5d05d6ed/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20200529172331-a64b76657301/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
gomod.garykim.dev/nc-talk v0.0.1 h1:6mgjcAf5/HMkV0CFGeXVfYHG7FAUCQcGR8eg9oM6fCc=
 | 
			
		||||
gomod.garykim.dev/nc-talk v0.0.1/go.mod h1:0/Ksg0osAYmnWKs1OcCG+gBQ4HU1xiF1699g9B6jWZw=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 | 
			
		||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 | 
			
		||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 | 
			
		||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
 | 
			
		||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
			
		||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
 | 
			
		||||
@@ -341,7 +490,12 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
			
		||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
			
		||||
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/42wim/matterbridge/bridge/config"
 | 
			
		||||
@@ -15,7 +16,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	version = "1.17.2"
 | 
			
		||||
	version = "1.18.0"
 | 
			
		||||
	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
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -1203,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
 | 
			
		||||
 | 
			
		||||
@@ -1368,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"
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
#
 | 
			
		||||
@@ -1589,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
 | 
			
		||||
###################################################################
 | 
			
		||||
@@ -1707,6 +1745,8 @@ enable=true
 | 
			
		||||
    # -------------------------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    #   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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -107,7 +107,7 @@ 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: %s", err.Error())
 | 
			
		||||
@@ -141,11 +141,11 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 ./...
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/d5/tengo/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -5,8 +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)
 | 
			
		||||
 | 
			
		||||
**Tengo is a small, dynamic, fast, secure script language for Go.** 
 | 
			
		||||
 | 
			
		||||
@@ -51,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)_  
 | 
			
		||||
@@ -136,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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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:])
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								vendor/github.com/d5/tengo/v2/vm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/d5/tengo/v2/vm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/fsnotify/fsnotify/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/fsnotify/fsnotify/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,5 +1,12 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
[*.go]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
indent_size = 4
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
 | 
			
		||||
[*.{yml,yaml}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
go.sum linguist-generated
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/fsnotify/fsnotify/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/fsnotify/fsnotify/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -2,29 +2,35 @@ sudo: false
 | 
			
		||||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
  - 1.8.x
 | 
			
		||||
  - 1.9.x
 | 
			
		||||
  - tip
 | 
			
		||||
  - "stable"
 | 
			
		||||
  - "1.11.x"
 | 
			
		||||
  - "1.10.x"
 | 
			
		||||
  - "1.9.x"
 | 
			
		||||
 | 
			
		||||
matrix:
 | 
			
		||||
  include:
 | 
			
		||||
    - go: "stable"
 | 
			
		||||
      env: GOLINT=true
 | 
			
		||||
  allow_failures:
 | 
			
		||||
    - go: tip
 | 
			
		||||
  fast_finish: true
 | 
			
		||||
 | 
			
		||||
before_script:
 | 
			
		||||
  - go get -u github.com/golang/lint/golint
 | 
			
		||||
 | 
			
		||||
before_install:
 | 
			
		||||
  - if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - go test -v --race ./...
 | 
			
		||||
  - go test --race ./...
 | 
			
		||||
 | 
			
		||||
after_script:
 | 
			
		||||
  - test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
 | 
			
		||||
  - test -z "$(golint ./...     | tee /dev/stderr)"
 | 
			
		||||
  - if [ ! -z  "${GOLINT}" ]; then echo running golint; golint --set_exit_status  ./...; else echo skipping golint; fi
 | 
			
		||||
  - go vet ./...
 | 
			
		||||
 | 
			
		||||
os:
 | 
			
		||||
  - linux
 | 
			
		||||
  - osx
 | 
			
		||||
  - windows
 | 
			
		||||
 | 
			
		||||
notifications:
 | 
			
		||||
  email: false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
Copyright (c) 2012 The Go Authors. All rights reserved.
 | 
			
		||||
Copyright (c) 2012 fsnotify Authors. All rights reserved.
 | 
			
		||||
Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -10,16 +10,16 @@ go get -u golang.org/x/sys/...
 | 
			
		||||
 | 
			
		||||
Cross platform: Windows, Linux, BSD and macOS.
 | 
			
		||||
 | 
			
		||||
|Adapter   |OS        |Status    |
 | 
			
		||||
|----------|----------|----------|
 | 
			
		||||
|inotify   |Linux 2.6.27 or later, Android\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
 | 
			
		||||
|kqueue    |BSD, macOS, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
 | 
			
		||||
|ReadDirectoryChangesW|Windows|Supported [](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
 | 
			
		||||
|FSEvents  |macOS         |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
 | 
			
		||||
|FEN       |Solaris 11    |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
 | 
			
		||||
|fanotify  |Linux 2.6.37+ | |
 | 
			
		||||
|USN Journals |Windows    |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
 | 
			
		||||
|Polling   |*All*         |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
 | 
			
		||||
| Adapter               | OS                               | Status                                                                                                                          |
 | 
			
		||||
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| inotify               | Linux 2.6.27 or later, Android\* | Supported [](https://travis-ci.org/fsnotify/fsnotify) |
 | 
			
		||||
| kqueue                | BSD, macOS, iOS\*                | Supported [](https://travis-ci.org/fsnotify/fsnotify) |
 | 
			
		||||
| ReadDirectoryChangesW | Windows                          | Supported [](https://travis-ci.org/fsnotify/fsnotify) |
 | 
			
		||||
| FSEvents              | macOS                            | [Planned](https://github.com/fsnotify/fsnotify/issues/11)                                                                       |
 | 
			
		||||
| FEN                   | Solaris 11                       | [In Progress](https://github.com/fsnotify/fsnotify/issues/12)                                                                   |
 | 
			
		||||
| fanotify              | Linux 2.6.37+                    | [Planned](https://github.com/fsnotify/fsnotify/issues/114)                                                                      |
 | 
			
		||||
| USN Journals          | Windows                          | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)                                                                         |
 | 
			
		||||
| Polling               | *All*                            | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)                                                                          |
 | 
			
		||||
 | 
			
		||||
\* Android and iOS are untested.
 | 
			
		||||
 | 
			
		||||
@@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o
 | 
			
		||||
 | 
			
		||||
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/fsnotify/fsnotify"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	watcher, err := fsnotify.NewWatcher()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer watcher.Close()
 | 
			
		||||
 | 
			
		||||
	done := make(chan bool)
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case event, ok := <-watcher.Events:
 | 
			
		||||
				if !ok {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				log.Println("event:", event)
 | 
			
		||||
				if event.Op&fsnotify.Write == fsnotify.Write {
 | 
			
		||||
					log.Println("modified file:", event.Name)
 | 
			
		||||
				}
 | 
			
		||||
			case err, ok := <-watcher.Errors:
 | 
			
		||||
				if !ok {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				log.Println("error:", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	err = watcher.Add("/tmp/foo")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	<-done
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
 | 
			
		||||
@@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created:
 | 
			
		||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
 | 
			
		||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
 | 
			
		||||
 | 
			
		||||
**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
 | 
			
		||||
 | 
			
		||||
fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
 | 
			
		||||
 | 
			
		||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
 | 
			
		||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
 | 
			
		||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -63,4 +63,6 @@ func (e Event) String() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common errors that can be reported by a watcher
 | 
			
		||||
var ErrEventOverflow = errors.New("fsnotify queue overflow")
 | 
			
		||||
var (
 | 
			
		||||
	ErrEventOverflow = errors.New("fsnotify queue overflow")
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/fsnotify/fsnotify/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/fsnotify/fsnotify/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
module github.com/fsnotify/fsnotify
 | 
			
		||||
 | 
			
		||||
go 1.13
 | 
			
		||||
 | 
			
		||||
require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) {
 | 
			
		||||
	poller.fd = fd
 | 
			
		||||
 | 
			
		||||
	// Create epoll fd
 | 
			
		||||
	poller.epfd, errno = unix.EpollCreate1(0)
 | 
			
		||||
	poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
 | 
			
		||||
	if poller.epfd == -1 {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
	// Create pipe; pipe[0] is the read end, pipe[1] the write end.
 | 
			
		||||
	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
 | 
			
		||||
	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
 | 
			
		||||
	if errno != nil {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -8,4 +8,4 @@ package fsnotify
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
const openMode = unix.O_NONBLOCK | unix.O_RDONLY
 | 
			
		||||
const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -9,4 +9,4 @@ package fsnotify
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
// note: this constant is not defined on BSD
 | 
			
		||||
const openMode = unix.O_EVTONLY
 | 
			
		||||
const openMode = unix.O_EVTONLY | unix.O_CLOEXEC
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,4 +3,6 @@ language: go
 | 
			
		||||
go:
 | 
			
		||||
  - '1.10'
 | 
			
		||||
  - '1.11'
 | 
			
		||||
  - '1.12'
 | 
			
		||||
  - '1.13'
 | 
			
		||||
  - tip
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -3,7 +3,7 @@
 | 
			
		||||
[](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api)
 | 
			
		||||
[](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api)
 | 
			
		||||
 | 
			
		||||
All methods are fairly self explanatory, and reading the godoc page should
 | 
			
		||||
All methods are fairly self explanatory, and reading the [godoc](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api) page should
 | 
			
		||||
explain everything. If something isn't clear, open an issue or submit
 | 
			
		||||
a pull request.
 | 
			
		||||
 | 
			
		||||
@@ -63,7 +63,7 @@ func main() {
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
There are more examples on the [wiki](https://github.com/go-telegram-bot-api/telegram-bot-api/wiki)
 | 
			
		||||
with detailed information on how to do many differen kinds of things.
 | 
			
		||||
with detailed information on how to do many different kinds of things.
 | 
			
		||||
It's a great place to get started on using keyboards, commands, or other
 | 
			
		||||
kinds of reply markup.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -19,34 +19,50 @@ import (
 | 
			
		||||
	"github.com/technoweenie/multipartstreamer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type HttpClient interface {
 | 
			
		||||
	Do(req *http.Request) (*http.Response, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BotAPI allows you to interact with the Telegram Bot API.
 | 
			
		||||
type BotAPI struct {
 | 
			
		||||
	Token  string `json:"token"`
 | 
			
		||||
	Debug  bool   `json:"debug"`
 | 
			
		||||
	Buffer int    `json:"buffer"`
 | 
			
		||||
 | 
			
		||||
	Self   User         `json:"-"`
 | 
			
		||||
	Client *http.Client `json:"-"`
 | 
			
		||||
	Self            User       `json:"-"`
 | 
			
		||||
	Client          HttpClient `json:"-"`
 | 
			
		||||
	shutdownChannel chan interface{}
 | 
			
		||||
 | 
			
		||||
	apiEndpoint string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBotAPI creates a new BotAPI instance.
 | 
			
		||||
//
 | 
			
		||||
// It requires a token, provided by @BotFather on Telegram.
 | 
			
		||||
func NewBotAPI(token string) (*BotAPI, error) {
 | 
			
		||||
	return NewBotAPIWithClient(token, &http.Client{})
 | 
			
		||||
	return NewBotAPIWithClient(token, APIEndpoint, &http.Client{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBotAPIWithAPIEndpoint creates a new BotAPI instance
 | 
			
		||||
// and allows you to pass API endpoint.
 | 
			
		||||
//
 | 
			
		||||
// It requires a token, provided by @BotFather on Telegram and API endpoint.
 | 
			
		||||
func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) {
 | 
			
		||||
	return NewBotAPIWithClient(token, apiEndpoint, &http.Client{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBotAPIWithClient creates a new BotAPI instance
 | 
			
		||||
// and allows you to pass a http.Client.
 | 
			
		||||
//
 | 
			
		||||
// It requires a token, provided by @BotFather on Telegram.
 | 
			
		||||
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
 | 
			
		||||
// It requires a token, provided by @BotFather on Telegram and API endpoint.
 | 
			
		||||
func NewBotAPIWithClient(token, apiEndpoint string, client HttpClient) (*BotAPI, error) {
 | 
			
		||||
	bot := &BotAPI{
 | 
			
		||||
		Token:  token,
 | 
			
		||||
		Client: client,
 | 
			
		||||
		Buffer: 100,
 | 
			
		||||
		Token:           token,
 | 
			
		||||
		Client:          client,
 | 
			
		||||
		Buffer:          100,
 | 
			
		||||
		shutdownChannel: make(chan interface{}),
 | 
			
		||||
 | 
			
		||||
		apiEndpoint: apiEndpoint,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	self, err := bot.GetMe()
 | 
			
		||||
@@ -59,11 +75,21 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
 | 
			
		||||
	return bot, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *BotAPI) SetAPIEndpoint(apiEndpoint string) {
 | 
			
		||||
	b.apiEndpoint = apiEndpoint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeRequest makes a request to a specific endpoint with our token.
 | 
			
		||||
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
 | 
			
		||||
	method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
 | 
			
		||||
	method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
 | 
			
		||||
 | 
			
		||||
	resp, err := bot.Client.PostForm(method, params)
 | 
			
		||||
	req, err := http.NewRequest("POST", method, strings.NewReader(params.Encode()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return APIResponse{}, err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
	resp, err := bot.Client.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return APIResponse{}, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -84,7 +110,7 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
 | 
			
		||||
		if apiResp.Parameters != nil {
 | 
			
		||||
			parameters = *apiResp.Parameters
 | 
			
		||||
		}
 | 
			
		||||
		return apiResp, Error{apiResp.Description, parameters}
 | 
			
		||||
		return apiResp, &Error{Code: apiResp.ErrorCode, Message: apiResp.Description, ResponseParameters: parameters}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return apiResp, nil
 | 
			
		||||
@@ -186,7 +212,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna
 | 
			
		||||
		return APIResponse{}, errors.New(ErrBadFileType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
 | 
			
		||||
	method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("POST", method, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -430,7 +456,7 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
 | 
			
		||||
 | 
			
		||||
// RemoveWebhook unsets the webhook.
 | 
			
		||||
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
 | 
			
		||||
	return bot.MakeRequest("setWebhook", url.Values{})
 | 
			
		||||
	return bot.MakeRequest("deleteWebhook", url.Values{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetWebhook sets a webhook.
 | 
			
		||||
@@ -487,6 +513,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-bot.shutdownChannel:
 | 
			
		||||
				close(ch)
 | 
			
		||||
				return
 | 
			
		||||
			default:
 | 
			
		||||
			}
 | 
			
		||||
@@ -966,3 +993,22 @@ func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, e
 | 
			
		||||
 | 
			
		||||
	return bot.MakeRequest(config.method(), v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStickerSet get a sticker set.
 | 
			
		||||
func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
 | 
			
		||||
	v, err := config.values()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return StickerSet{}, err
 | 
			
		||||
	}
 | 
			
		||||
	bot.debugLog(config.method(), v, nil)
 | 
			
		||||
	res, err := bot.MakeRequest(config.method(), v)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return StickerSet{}, err
 | 
			
		||||
	}
 | 
			
		||||
	stickerSet := StickerSet{}
 | 
			
		||||
	err = json.Unmarshal(res.Result, &stickerSet)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return StickerSet{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return stickerSet, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1262,3 +1262,18 @@ func (config DeleteChatPhotoConfig) values() (url.Values, error) {
 | 
			
		||||
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStickerSetConfig contains information for get sticker set.
 | 
			
		||||
type GetStickerSetConfig struct {
 | 
			
		||||
	Name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (config GetStickerSetConfig) method() string {
 | 
			
		||||
	return "getStickerSet"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (config GetStickerSetConfig) values() (url.Values, error) {
 | 
			
		||||
	v := url.Values{}
 | 
			
		||||
	v.Add("name", config.Name)
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
module github.com/go-telegram-bot-api/telegram-bot-api
 | 
			
		||||
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require github.com/technoweenie/multipartstreamer v1.0.1
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
 | 
			
		||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -29,7 +29,8 @@ func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
 | 
			
		||||
// NewMessageToChannel creates a new Message that is sent to a channel
 | 
			
		||||
// by username.
 | 
			
		||||
//
 | 
			
		||||
// username is the username of the channel, text is the message text.
 | 
			
		||||
// username is the username of the channel, text is the message text,
 | 
			
		||||
// and the username should be in the form of `@username`.
 | 
			
		||||
func NewMessageToChannel(username string, text string) MessageConfig {
 | 
			
		||||
	return MessageConfig{
 | 
			
		||||
		BaseChat: BaseChat{
 | 
			
		||||
@@ -604,6 +605,18 @@ func NewInlineQueryResultLocation(id, title string, latitude, longitude float64)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInlineQueryResultVenue creates a new inline query venue.
 | 
			
		||||
func NewInlineQueryResultVenue(id, title, address string, latitude, longitude float64) InlineQueryResultVenue {
 | 
			
		||||
	return InlineQueryResultVenue{
 | 
			
		||||
		Type:      "venue",
 | 
			
		||||
		ID:        id,
 | 
			
		||||
		Title:     title,
 | 
			
		||||
		Address:   address,
 | 
			
		||||
		Latitude:  latitude,
 | 
			
		||||
		Longitude: longitude,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewEditMessageText allows you to edit the text of a message.
 | 
			
		||||
func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTextConfig {
 | 
			
		||||
	return EditMessageTextConfig{
 | 
			
		||||
@@ -622,7 +635,7 @@ func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMess
 | 
			
		||||
			ChatID:    chatID,
 | 
			
		||||
			MessageID: messageID,
 | 
			
		||||
		},
 | 
			
		||||
		Caption:   caption,
 | 
			
		||||
		Caption: caption,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										124
									
								
								vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -64,6 +64,9 @@ type User struct {
 | 
			
		||||
// It is normally a user's username, but falls back to a first/last
 | 
			
		||||
// name as available.
 | 
			
		||||
func (u *User) String() string {
 | 
			
		||||
	if u == nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	if u.UserName != "" {
 | 
			
		||||
		return u.UserName
 | 
			
		||||
	}
 | 
			
		||||
@@ -100,6 +103,7 @@ type Chat struct {
 | 
			
		||||
	Photo               *ChatPhoto `json:"photo"`
 | 
			
		||||
	Description         string     `json:"description,omitempty"` // optional
 | 
			
		||||
	InviteLink          string     `json:"invite_link,omitempty"` // optional
 | 
			
		||||
	PinnedMessage       *Message   `json:"pinned_message"`        // optional
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsPrivate returns if the Chat is a private conversation.
 | 
			
		||||
@@ -142,6 +146,7 @@ type Message struct {
 | 
			
		||||
	EditDate              int                `json:"edit_date"`               // optional
 | 
			
		||||
	Text                  string             `json:"text"`                    // optional
 | 
			
		||||
	Entities              *[]MessageEntity   `json:"entities"`                // optional
 | 
			
		||||
	CaptionEntities       *[]MessageEntity   `json:"caption_entities"`        // optional
 | 
			
		||||
	Audio                 *Audio             `json:"audio"`                   // optional
 | 
			
		||||
	Document              *Document          `json:"document"`                // optional
 | 
			
		||||
	Animation             *ChatAnimation     `json:"animation"`               // optional
 | 
			
		||||
@@ -183,7 +188,7 @@ func (m *Message) IsCommand() bool {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entity := (*m.Entities)[0]
 | 
			
		||||
	return entity.Offset == 0 && entity.Type == "bot_command"
 | 
			
		||||
	return entity.Offset == 0 && entity.IsCommand()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Command checks if the message was a command and if it was, returns the
 | 
			
		||||
@@ -249,12 +254,62 @@ type MessageEntity struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseURL attempts to parse a URL contained within a MessageEntity.
 | 
			
		||||
func (entity MessageEntity) ParseURL() (*url.URL, error) {
 | 
			
		||||
	if entity.URL == "" {
 | 
			
		||||
func (e MessageEntity) ParseURL() (*url.URL, error) {
 | 
			
		||||
	if e.URL == "" {
 | 
			
		||||
		return nil, errors.New(ErrBadURL)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return url.Parse(entity.URL)
 | 
			
		||||
	return url.Parse(e.URL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsMention returns true if the type of the message entity is "mention" (@username).
 | 
			
		||||
func (e MessageEntity) IsMention() bool {
 | 
			
		||||
	return e.Type == "mention"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsHashtag returns true if the type of the message entity is "hashtag".
 | 
			
		||||
func (e MessageEntity) IsHashtag() bool {
 | 
			
		||||
	return e.Type == "hashtag"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsCommand returns true if the type of the message entity is "bot_command".
 | 
			
		||||
func (e MessageEntity) IsCommand() bool {
 | 
			
		||||
	return e.Type == "bot_command"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsUrl returns true if the type of the message entity is "url".
 | 
			
		||||
func (e MessageEntity) IsUrl() bool {
 | 
			
		||||
	return e.Type == "url"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmail returns true if the type of the message entity is "email".
 | 
			
		||||
func (e MessageEntity) IsEmail() bool {
 | 
			
		||||
	return e.Type == "email"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsBold returns true if the type of the message entity is "bold" (bold text).
 | 
			
		||||
func (e MessageEntity) IsBold() bool {
 | 
			
		||||
	return e.Type == "bold"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsItalic returns true if the type of the message entity is "italic" (italic text).
 | 
			
		||||
func (e MessageEntity) IsItalic() bool {
 | 
			
		||||
	return e.Type == "italic"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsCode returns true if the type of the message entity is "code" (monowidth string).
 | 
			
		||||
func (e MessageEntity) IsCode() bool {
 | 
			
		||||
	return e.Type == "code"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsPre returns true if the type of the message entity is "pre" (monowidth block).
 | 
			
		||||
func (e MessageEntity) IsPre() bool {
 | 
			
		||||
	return e.Type == "pre"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTextLink returns true if the type of the message entity is "text_link" (clickable text URL).
 | 
			
		||||
func (e MessageEntity) IsTextLink() bool {
 | 
			
		||||
	return e.Type == "text_link"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PhotoSize contains information about photos.
 | 
			
		||||
@@ -286,13 +341,23 @@ type Document struct {
 | 
			
		||||
 | 
			
		||||
// Sticker contains information about a sticker.
 | 
			
		||||
type Sticker struct {
 | 
			
		||||
	FileID    string     `json:"file_id"`
 | 
			
		||||
	Width     int        `json:"width"`
 | 
			
		||||
	Height    int        `json:"height"`
 | 
			
		||||
	Thumbnail *PhotoSize `json:"thumb"`     // optional
 | 
			
		||||
	Emoji     string     `json:"emoji"`     // optional
 | 
			
		||||
	FileSize  int        `json:"file_size"` // optional
 | 
			
		||||
	SetName   string     `json:"set_name"`  // optional
 | 
			
		||||
	FileUniqueID string     `json:"file_unique_id"`
 | 
			
		||||
	FileID       string     `json:"file_id"`
 | 
			
		||||
	Width        int        `json:"width"`
 | 
			
		||||
	Height       int        `json:"height"`
 | 
			
		||||
	Thumbnail    *PhotoSize `json:"thumb"`       // optional
 | 
			
		||||
	Emoji        string     `json:"emoji"`       // optional
 | 
			
		||||
	FileSize     int        `json:"file_size"`   // optional
 | 
			
		||||
	SetName      string     `json:"set_name"`    // optional
 | 
			
		||||
	IsAnimated   bool       `json:"is_animated"` // optional
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StickerSet struct {
 | 
			
		||||
	Name          string    `json:"name"`
 | 
			
		||||
	Title         string    `json:"title"`
 | 
			
		||||
	IsAnimated    bool      `json:"is_animated"`
 | 
			
		||||
	ContainsMasks bool      `json:"contains_masks"`
 | 
			
		||||
	Stickers      []Sticker `json:"stickers"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChatAnimation contains information about an animation.
 | 
			
		||||
@@ -582,6 +647,7 @@ type InlineQueryResultPhoto struct {
 | 
			
		||||
	Title               string                `json:"title"`
 | 
			
		||||
	Description         string                `json:"description"`
 | 
			
		||||
	Caption             string                `json:"caption"`
 | 
			
		||||
	ParseMode           string                `json:"parse_mode"`
 | 
			
		||||
	ReplyMarkup         *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
 | 
			
		||||
	InputMessageContent interface{}           `json:"input_message_content,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
@@ -601,15 +667,15 @@ type InlineQueryResultCachedPhoto struct {
 | 
			
		||||
 | 
			
		||||
// InlineQueryResultGIF is an inline query response GIF.
 | 
			
		||||
type InlineQueryResultGIF struct {
 | 
			
		||||
	Type                string                `json:"type"`    // required
 | 
			
		||||
	ID                  string                `json:"id"`      // required
 | 
			
		||||
	URL                 string                `json:"gif_url"` // required
 | 
			
		||||
	Width               int                   `json:"gif_width"`
 | 
			
		||||
	Height              int                   `json:"gif_height"`
 | 
			
		||||
	Duration            int                   `json:"gif_duration"`
 | 
			
		||||
	ThumbURL            string                `json:"thumb_url"`
 | 
			
		||||
	Title               string                `json:"title"`
 | 
			
		||||
	Caption             string                `json:"caption"`
 | 
			
		||||
	Type                string                `json:"type"`      // required
 | 
			
		||||
	ID                  string                `json:"id"`        // required
 | 
			
		||||
	URL                 string                `json:"gif_url"`   // required
 | 
			
		||||
	ThumbURL            string                `json:"thumb_url"` // required
 | 
			
		||||
	Width               int                   `json:"gif_width,omitempty"`
 | 
			
		||||
	Height              int                   `json:"gif_height,omitempty"`
 | 
			
		||||
	Duration            int                   `json:"gif_duration,omitempty"`
 | 
			
		||||
	Title               string                `json:"title,omitempty"`
 | 
			
		||||
	Caption             string                `json:"caption,omitempty"`
 | 
			
		||||
	ReplyMarkup         *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
 | 
			
		||||
	InputMessageContent interface{}           `json:"input_message_content,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
@@ -775,6 +841,23 @@ type InlineQueryResultLocation struct {
 | 
			
		||||
	ThumbHeight         int                   `json:"thumb_height"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InlineQueryResultVenue is an inline query response venue.
 | 
			
		||||
type InlineQueryResultVenue struct {
 | 
			
		||||
	Type                string                `json:"type"`      // required
 | 
			
		||||
	ID                  string                `json:"id"`        // required
 | 
			
		||||
	Latitude            float64               `json:"latitude"`  // required
 | 
			
		||||
	Longitude           float64               `json:"longitude"` // required
 | 
			
		||||
	Title               string                `json:"title"`     // required
 | 
			
		||||
	Address             string                `json:"address"`   // required
 | 
			
		||||
	FoursquareID        string                `json:"foursquare_id"`
 | 
			
		||||
	FoursquareType      string                `json:"foursquare_type"`
 | 
			
		||||
	ReplyMarkup         *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
 | 
			
		||||
	InputMessageContent interface{}           `json:"input_message_content,omitempty"`
 | 
			
		||||
	ThumbURL            string                `json:"thumb_url"`
 | 
			
		||||
	ThumbWidth          int                   `json:"thumb_width"`
 | 
			
		||||
	ThumbHeight         int                   `json:"thumb_height"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InlineQueryResultGame is an inline query response game.
 | 
			
		||||
type InlineQueryResultGame struct {
 | 
			
		||||
	Type          string                `json:"type"`
 | 
			
		||||
@@ -897,6 +980,7 @@ type PreCheckoutQuery struct {
 | 
			
		||||
 | 
			
		||||
// Error is an error containing extra information returned by the Telegram API.
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Code    int
 | 
			
		||||
	Message string
 | 
			
		||||
	ResponseParameters
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/golang/protobuf/proto/properties.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/golang/protobuf/proto/properties.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -38,7 +38,6 @@ package proto
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
@@ -194,7 +193,7 @@ func (p *Properties) Parse(s string) {
 | 
			
		||||
	// "bytes,49,opt,name=foo,def=hello!"
 | 
			
		||||
	fields := strings.Split(s, ",") // breaks def=, but handled below.
 | 
			
		||||
	if len(fields) < 2 {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
 | 
			
		||||
		log.Printf("proto: tag has too few fields: %q", s)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +213,7 @@ func (p *Properties) Parse(s string) {
 | 
			
		||||
		p.WireType = WireBytes
 | 
			
		||||
		// no numeric converter for non-numeric types
 | 
			
		||||
	default:
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
 | 
			
		||||
		log.Printf("proto: tag has unknown wire type: %q", s)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/gomarkdown/markdown/html/renderer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/gomarkdown/markdown/html/renderer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -28,6 +28,7 @@ const (
 | 
			
		||||
	Safelink                                  // Only link to trusted protocols
 | 
			
		||||
	NofollowLinks                             // Only link with rel="nofollow"
 | 
			
		||||
	NoreferrerLinks                           // Only link with rel="noreferrer"
 | 
			
		||||
	NoopenerLinks                             // Only link with rel="noopener"
 | 
			
		||||
	HrefTargetBlank                           // Add a blank target
 | 
			
		||||
	CompletePage                              // Generate a complete HTML page
 | 
			
		||||
	UseXHTML                                  // Generate XHTML output instead of HTML
 | 
			
		||||
@@ -295,6 +296,9 @@ func appendLinkAttrs(attrs []string, flags Flags, link []byte) []string {
 | 
			
		||||
	if flags&NoreferrerLinks != 0 {
 | 
			
		||||
		val = append(val, "noreferrer")
 | 
			
		||||
	}
 | 
			
		||||
	if flags&NoopenerLinks != 0 {
 | 
			
		||||
		val = append(val, "noopener")
 | 
			
		||||
	}
 | 
			
		||||
	if flags&HrefTargetBlank != 0 {
 | 
			
		||||
		attrs = append(attrs, `target="_blank"`)
 | 
			
		||||
	}
 | 
			
		||||
@@ -1110,7 +1114,9 @@ func (r *Renderer) writeTOC(w io.Writer, doc ast.Node) {
 | 
			
		||||
				buf.WriteString("</a>")
 | 
			
		||||
				return ast.GoToNext
 | 
			
		||||
			}
 | 
			
		||||
			nodeData.HeadingID = fmt.Sprintf("toc_%d", headingCount)
 | 
			
		||||
			if nodeData.HeadingID == "" {
 | 
			
		||||
				nodeData.HeadingID = fmt.Sprintf("toc_%d", headingCount)
 | 
			
		||||
			}
 | 
			
		||||
			if nodeData.Level == tocLevel {
 | 
			
		||||
				buf.WriteString("</li>\n\n<li>")
 | 
			
		||||
			} else if nodeData.Level < tocLevel {
 | 
			
		||||
@@ -1126,7 +1132,7 @@ func (r *Renderer) writeTOC(w io.Writer, doc ast.Node) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fmt.Fprintf(&buf, `<a href="#toc_%d">`, headingCount)
 | 
			
		||||
			fmt.Fprintf(&buf, `<a href="#%s">`, nodeData.HeadingID)
 | 
			
		||||
			headingCount++
 | 
			
		||||
			return ast.GoToNext
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/gomarkdown/markdown/parser/inline.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/gomarkdown/markdown/parser/inline.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -326,6 +326,7 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
 | 
			
		||||
		i = skipSpace(data, i)
 | 
			
		||||
 | 
			
		||||
		linkB := i
 | 
			
		||||
		brace := 0
 | 
			
		||||
 | 
			
		||||
		// look for link end: ' " )
 | 
			
		||||
	findlinkend:
 | 
			
		||||
@@ -334,7 +335,18 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
 | 
			
		||||
			case data[i] == '\\':
 | 
			
		||||
				i += 2
 | 
			
		||||
 | 
			
		||||
			case data[i] == ')' || data[i] == '\'' || data[i] == '"':
 | 
			
		||||
			case data[i] == '(':
 | 
			
		||||
				brace++
 | 
			
		||||
				i++
 | 
			
		||||
 | 
			
		||||
			case data[i] == ')':
 | 
			
		||||
				if brace <= 0 {
 | 
			
		||||
					break findlinkend
 | 
			
		||||
				}
 | 
			
		||||
				brace--
 | 
			
		||||
				i++
 | 
			
		||||
 | 
			
		||||
			case data[i] == '\'' || data[i] == '"':
 | 
			
		||||
				break findlinkend
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
@@ -352,19 +364,21 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
 | 
			
		||||
		if data[i] == '\'' || data[i] == '"' {
 | 
			
		||||
			i++
 | 
			
		||||
			titleB = i
 | 
			
		||||
			titleEndCharFound := false
 | 
			
		||||
 | 
			
		||||
		findtitleend:
 | 
			
		||||
			for i < len(data) {
 | 
			
		||||
				switch {
 | 
			
		||||
				case data[i] == '\\':
 | 
			
		||||
					i += 2
 | 
			
		||||
 | 
			
		||||
				case data[i] == ')':
 | 
			
		||||
					break findtitleend
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					i++
 | 
			
		||||
 | 
			
		||||
				case data[i] == data[titleB-1]: // matching title delimiter
 | 
			
		||||
					titleEndCharFound = true
 | 
			
		||||
 | 
			
		||||
				case titleEndCharFound && data[i] == ')':
 | 
			
		||||
					break findtitleend
 | 
			
		||||
				}
 | 
			
		||||
				i++
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if i >= len(data) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/google/gops/agent/agent.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/google/gops/agent/agent.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -20,12 +20,13 @@ import (
 | 
			
		||||
	"runtime/pprof"
 | 
			
		||||
	"runtime/trace"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/gops/internal"
 | 
			
		||||
	"github.com/google/gops/signal"
 | 
			
		||||
	"github.com/kardianos/osext"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const defaultAddr = "127.0.0.1:0"
 | 
			
		||||
@@ -116,18 +117,21 @@ func listen() {
 | 
			
		||||
	for {
 | 
			
		||||
		fd, err := listener.Accept()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "gops: %v", err)
 | 
			
		||||
			// No great way to check for this, see https://golang.org/issues/4373.
 | 
			
		||||
			if !strings.Contains(err.Error(), "use of closed network connection") {
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "gops: %v\n", err)
 | 
			
		||||
			}
 | 
			
		||||
			if netErr, ok := err.(net.Error); ok && !netErr.Temporary() {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := fd.Read(buf); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "gops: %v", err)
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "gops: %v\n", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err := handle(fd, buf); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "gops: %v", err)
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "gops: %v\n", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		fd.Close()
 | 
			
		||||
@@ -136,12 +140,16 @@ func listen() {
 | 
			
		||||
 | 
			
		||||
func gracefulShutdown() {
 | 
			
		||||
	c := make(chan os.Signal, 1)
 | 
			
		||||
	gosignal.Notify(c, os.Interrupt)
 | 
			
		||||
	gosignal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
 | 
			
		||||
	go func() {
 | 
			
		||||
		// cleanup the socket on shutdown.
 | 
			
		||||
		<-c
 | 
			
		||||
		sig := <-c
 | 
			
		||||
		Close()
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
		ret := 1
 | 
			
		||||
		if sig == syscall.SIGTERM {
 | 
			
		||||
			ret = 0
 | 
			
		||||
		}
 | 
			
		||||
		os.Exit(ret)
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -220,7 +228,7 @@ func handle(conn io.ReadWriter, msg []byte) error {
 | 
			
		||||
	case signal.Version:
 | 
			
		||||
		fmt.Fprintf(conn, "%v\n", runtime.Version())
 | 
			
		||||
	case signal.HeapProfile:
 | 
			
		||||
		pprof.WriteHeapProfile(conn)
 | 
			
		||||
		return pprof.WriteHeapProfile(conn)
 | 
			
		||||
	case signal.CPUProfile:
 | 
			
		||||
		if err := pprof.StartCPUProfile(conn); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
@@ -233,7 +241,7 @@ func handle(conn io.ReadWriter, msg []byte) error {
 | 
			
		||||
		fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0))
 | 
			
		||||
		fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU())
 | 
			
		||||
	case signal.BinaryDump:
 | 
			
		||||
		path, err := osext.Executable()
 | 
			
		||||
		path, err := os.Executable()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -246,7 +254,9 @@ func handle(conn io.ReadWriter, msg []byte) error {
 | 
			
		||||
		_, err = bufio.NewReader(f).WriteTo(conn)
 | 
			
		||||
		return err
 | 
			
		||||
	case signal.Trace:
 | 
			
		||||
		trace.Start(conn)
 | 
			
		||||
		if err := trace.Start(conn); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		time.Sleep(5 * time.Second)
 | 
			
		||||
		trace.Stop()
 | 
			
		||||
	case signal.SetGCPercent:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/google/gops/internal/internal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/google/gops/internal/internal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -6,12 +6,12 @@ package internal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/user"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -22,9 +22,18 @@ func ConfigDir() (string, error) {
 | 
			
		||||
		return configDir, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if osUserConfigDir := getOSUserConfigDir(); osUserConfigDir != "" {
 | 
			
		||||
		return filepath.Join(osUserConfigDir, "gops"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		return filepath.Join(os.Getenv("APPDATA"), "gops"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if xdgConfigDir := os.Getenv("XDG_CONFIG_HOME"); xdgConfigDir != "" {
 | 
			
		||||
		return filepath.Join(xdgConfigDir, "gops"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	homeDir := guessUnixHomeDir()
 | 
			
		||||
	if homeDir == "" {
 | 
			
		||||
		return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
 | 
			
		||||
@@ -45,7 +54,7 @@ func PIDFile(pid int) (string, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s/%d", gopsdir, pid), nil
 | 
			
		||||
	return filepath.Join(gopsdir, strconv.Itoa(pid)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetPort(pid int) (string, error) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/google/gops/internal/internal_go1_13.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/google/gops/internal/internal_go1_13.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
// Copyright 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.
 | 
			
		||||
 | 
			
		||||
// +build go1.13
 | 
			
		||||
 | 
			
		||||
package internal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getOSUserConfigDir() string {
 | 
			
		||||
	configDir, err := os.UserConfigDir()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return configDir
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/google/gops/internal/internal_lt_go1_13.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/google/gops/internal/internal_lt_go1_13.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// Copyright 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.
 | 
			
		||||
 | 
			
		||||
// +build !go1.13
 | 
			
		||||
 | 
			
		||||
package internal
 | 
			
		||||
 | 
			
		||||
func getOSUserConfigDir() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/gorilla/websocket/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/gorilla/websocket/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -8,7 +8,7 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
 | 
			
		||||
 | 
			
		||||
### Documentation
 | 
			
		||||
 | 
			
		||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
 | 
			
		||||
* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
 | 
			
		||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
 | 
			
		||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
 | 
			
		||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/gorilla/websocket/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -244,8 +244,8 @@ type Conn struct {
 | 
			
		||||
	subprotocol string
 | 
			
		||||
 | 
			
		||||
	// Write fields
 | 
			
		||||
	mu            chan bool // used as mutex to protect write to conn
 | 
			
		||||
	writeBuf      []byte    // frame is constructed in this buffer.
 | 
			
		||||
	mu            chan struct{} // used as mutex to protect write to conn
 | 
			
		||||
	writeBuf      []byte        // frame is constructed in this buffer.
 | 
			
		||||
	writePool     BufferPool
 | 
			
		||||
	writeBufSize  int
 | 
			
		||||
	writeDeadline time.Time
 | 
			
		||||
@@ -302,8 +302,8 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int,
 | 
			
		||||
		writeBuf = make([]byte, writeBufferSize)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mu := make(chan bool, 1)
 | 
			
		||||
	mu <- true
 | 
			
		||||
	mu := make(chan struct{}, 1)
 | 
			
		||||
	mu <- struct{}{}
 | 
			
		||||
	c := &Conn{
 | 
			
		||||
		isServer:               isServer,
 | 
			
		||||
		br:                     br,
 | 
			
		||||
@@ -377,7 +377,7 @@ func (c *Conn) read(n int) ([]byte, error) {
 | 
			
		||||
 | 
			
		||||
func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {
 | 
			
		||||
	<-c.mu
 | 
			
		||||
	defer func() { c.mu <- true }()
 | 
			
		||||
	defer func() { c.mu <- struct{}{} }()
 | 
			
		||||
 | 
			
		||||
	c.writeErrMu.Lock()
 | 
			
		||||
	err := c.writeErr
 | 
			
		||||
@@ -429,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
 | 
			
		||||
		maskBytes(key, 0, buf[6:])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d := time.Hour * 1000
 | 
			
		||||
	d := 1000 * time.Hour
 | 
			
		||||
	if !deadline.IsZero() {
 | 
			
		||||
		d = deadline.Sub(time.Now())
 | 
			
		||||
		if d < 0 {
 | 
			
		||||
@@ -444,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
 | 
			
		||||
	case <-timer.C:
 | 
			
		||||
		return errWriteTimeout
 | 
			
		||||
	}
 | 
			
		||||
	defer func() { c.mu <- true }()
 | 
			
		||||
	defer func() { c.mu <- struct{}{} }()
 | 
			
		||||
 | 
			
		||||
	c.writeErrMu.Lock()
 | 
			
		||||
	err := c.writeErr
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/gorilla/websocket/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -187,9 +187,9 @@
 | 
			
		||||
// than the largest message do not provide any benefit.
 | 
			
		||||
//
 | 
			
		||||
// Depending on the distribution of message sizes, setting the buffer size to
 | 
			
		||||
// to a value less than the maximum expected message size can greatly reduce
 | 
			
		||||
// memory use with a small impact on performance. Here's an example: If 99% of
 | 
			
		||||
// the messages are smaller than 256 bytes and the maximum message size is 512
 | 
			
		||||
// a value less than the maximum expected message size can greatly reduce memory
 | 
			
		||||
// use with a small impact on performance. Here's an example: If 99% of the
 | 
			
		||||
// messages are smaller than 256 bytes and the maximum message size is 512
 | 
			
		||||
// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls
 | 
			
		||||
// than a buffer size of 512 bytes. The memory savings is 50%.
 | 
			
		||||
//
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/gorilla/websocket/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/gorilla/websocket/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,2 +0,0 @@
 | 
			
		||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/gorilla/websocket/prepared.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/gorilla/websocket/prepared.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -73,8 +73,8 @@ func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
 | 
			
		||||
		// Prepare a frame using a 'fake' connection.
 | 
			
		||||
		// TODO: Refactor code in conn.go to allow more direct construction of
 | 
			
		||||
		// the frame.
 | 
			
		||||
		mu := make(chan bool, 1)
 | 
			
		||||
		mu <- true
 | 
			
		||||
		mu := make(chan struct{}, 1)
 | 
			
		||||
		mu <- struct{}{}
 | 
			
		||||
		var nc prepareConn
 | 
			
		||||
		c := &Conn{
 | 
			
		||||
			conn:                   &nc,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/hashicorp/golang-lru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/hashicorp/golang-lru/lru.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -37,7 +37,7 @@ func (c *Cache) Purge() {
 | 
			
		||||
	c.lock.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add adds a value to the cache.  Returns true if an eviction occurred.
 | 
			
		||||
// Add adds a value to the cache. Returns true if an eviction occurred.
 | 
			
		||||
func (c *Cache) Add(key, value interface{}) (evicted bool) {
 | 
			
		||||
	c.lock.Lock()
 | 
			
		||||
	evicted = c.lru.Add(key, value)
 | 
			
		||||
@@ -71,8 +71,8 @@ func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
 | 
			
		||||
	return value, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContainsOrAdd checks if a key is in the cache  without updating the
 | 
			
		||||
// recent-ness or deleting it for being stale,  and if not, adds the value.
 | 
			
		||||
// ContainsOrAdd checks if a key is in the cache without updating the
 | 
			
		||||
// recent-ness or deleting it for being stale, and if not, adds the value.
 | 
			
		||||
// Returns whether found and whether an eviction occurred.
 | 
			
		||||
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
 | 
			
		||||
	c.lock.Lock()
 | 
			
		||||
@@ -85,6 +85,22 @@ func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
 | 
			
		||||
	return false, evicted
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PeekOrAdd checks if a key is in the cache without updating the
 | 
			
		||||
// recent-ness or deleting it for being stale, and if not, adds the value.
 | 
			
		||||
// Returns whether found and whether an eviction occurred.
 | 
			
		||||
func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
 | 
			
		||||
	c.lock.Lock()
 | 
			
		||||
	defer c.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	previous, ok = c.lru.Peek(key)
 | 
			
		||||
	if ok {
 | 
			
		||||
		return previous, true, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	evicted = c.lru.Add(key, value)
 | 
			
		||||
	return nil, false, evicted
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove removes the provided key from the cache.
 | 
			
		||||
func (c *Cache) Remove(key interface{}) (present bool) {
 | 
			
		||||
	c.lock.Lock()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/kardianos/osext/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/kardianos/osext/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,27 +0,0 @@
 | 
			
		||||
Copyright (c) 2012 The Go Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/kardianos/osext/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/kardianos/osext/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
### Extensions to the "os" package.
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/kardianos/osext)
 | 
			
		||||
 | 
			
		||||
## Find the current Executable and ExecutableFolder.
 | 
			
		||||
 | 
			
		||||
As of go1.8 the Executable function may be found in `os`. The Executable function
 | 
			
		||||
in the std lib `os` package is used if available.
 | 
			
		||||
 | 
			
		||||
There is sometimes utility in finding the current executable file
 | 
			
		||||
that is running. This can be used for upgrading the current executable
 | 
			
		||||
or finding resources located relative to the executable file. Both
 | 
			
		||||
working directory and the os.Args[0] value are arbitrary and cannot
 | 
			
		||||
be relied on; os.Args[0] can be "faked".
 | 
			
		||||
 | 
			
		||||
Multi-platform and supports:
 | 
			
		||||
 * Linux
 | 
			
		||||
 * OS X
 | 
			
		||||
 * Windows
 | 
			
		||||
 * Plan 9
 | 
			
		||||
 * BSDs.
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/kardianos/osext/osext.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/kardianos/osext/osext.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,33 +0,0 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
// Extensions to the standard "os" package.
 | 
			
		||||
package osext // import "github.com/kardianos/osext"
 | 
			
		||||
 | 
			
		||||
import "path/filepath"
 | 
			
		||||
 | 
			
		||||
var cx, ce = executableClean()
 | 
			
		||||
 | 
			
		||||
func executableClean() (string, error) {
 | 
			
		||||
	p, err := executable()
 | 
			
		||||
	return filepath.Clean(p), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Executable returns an absolute path that can be used to
 | 
			
		||||
// re-invoke the current program.
 | 
			
		||||
// It may not be valid after the current program exits.
 | 
			
		||||
func Executable() (string, error) {
 | 
			
		||||
	return cx, ce
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns same path as Executable, returns just the folder
 | 
			
		||||
// path. Excludes the executable name and any trailing slash.
 | 
			
		||||
func ExecutableFolder() (string, error) {
 | 
			
		||||
	p, err := Executable()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return filepath.Dir(p), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								vendor/github.com/kardianos/osext/osext_go18.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/kardianos/osext/osext_go18.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,9 +0,0 @@
 | 
			
		||||
//+build go1.8,!openbsd
 | 
			
		||||
 | 
			
		||||
package osext
 | 
			
		||||
 | 
			
		||||
import "os"
 | 
			
		||||
 | 
			
		||||
func executable() (string, error) {
 | 
			
		||||
	return os.Executable()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/kardianos/osext/osext_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/kardianos/osext/osext_plan9.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
//+build !go1.8
 | 
			
		||||
 | 
			
		||||
package osext
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func executable() (string, error) {
 | 
			
		||||
	f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
	return syscall.Fd2path(int(f.Fd()))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								vendor/github.com/kardianos/osext/osext_procfs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/kardianos/osext/osext_procfs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,36 +0,0 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
// +build !go1.8,android !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly
 | 
			
		||||
 | 
			
		||||
package osext
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func executable() (string, error) {
 | 
			
		||||
	switch runtime.GOOS {
 | 
			
		||||
	case "linux", "android":
 | 
			
		||||
		const deletedTag = " (deleted)"
 | 
			
		||||
		execpath, err := os.Readlink("/proc/self/exe")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return execpath, err
 | 
			
		||||
		}
 | 
			
		||||
		execpath = strings.TrimSuffix(execpath, deletedTag)
 | 
			
		||||
		execpath = strings.TrimPrefix(execpath, deletedTag)
 | 
			
		||||
		return execpath, nil
 | 
			
		||||
	case "netbsd":
 | 
			
		||||
		return os.Readlink("/proc/curproc/exe")
 | 
			
		||||
	case "dragonfly":
 | 
			
		||||
		return os.Readlink("/proc/curproc/file")
 | 
			
		||||
	case "solaris":
 | 
			
		||||
		return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
 | 
			
		||||
	}
 | 
			
		||||
	return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								vendor/github.com/kardianos/osext/osext_sysctl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/kardianos/osext/osext_sysctl.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,126 +0,0 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
// +build !go1.8,darwin !go1.8,freebsd openbsd
 | 
			
		||||
 | 
			
		||||
package osext
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var initCwd, initCwdErr = os.Getwd()
 | 
			
		||||
 | 
			
		||||
func executable() (string, error) {
 | 
			
		||||
	var mib [4]int32
 | 
			
		||||
	switch runtime.GOOS {
 | 
			
		||||
	case "freebsd":
 | 
			
		||||
		mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
 | 
			
		||||
	case "darwin":
 | 
			
		||||
		mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
 | 
			
		||||
	case "openbsd":
 | 
			
		||||
		mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := uintptr(0)
 | 
			
		||||
	// Get length.
 | 
			
		||||
	_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
 | 
			
		||||
	if errNum != 0 {
 | 
			
		||||
		return "", errNum
 | 
			
		||||
	}
 | 
			
		||||
	if n == 0 { // This shouldn't happen.
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]byte, n)
 | 
			
		||||
	_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
 | 
			
		||||
	if errNum != 0 {
 | 
			
		||||
		return "", errNum
 | 
			
		||||
	}
 | 
			
		||||
	if n == 0 { // This shouldn't happen.
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var execPath string
 | 
			
		||||
	switch runtime.GOOS {
 | 
			
		||||
	case "openbsd":
 | 
			
		||||
		// buf now contains **argv, with pointers to each of the C-style
 | 
			
		||||
		// NULL terminated arguments.
 | 
			
		||||
		var args []string
 | 
			
		||||
		argv := uintptr(unsafe.Pointer(&buf[0]))
 | 
			
		||||
	Loop:
 | 
			
		||||
		for {
 | 
			
		||||
			argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
 | 
			
		||||
			if argp == nil {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			for i := 0; uintptr(i) < n; i++ {
 | 
			
		||||
				// we don't want the full arguments list
 | 
			
		||||
				if string(argp[i]) == " " {
 | 
			
		||||
					break Loop
 | 
			
		||||
				}
 | 
			
		||||
				if argp[i] != 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				args = append(args, string(argp[:i]))
 | 
			
		||||
				n -= uintptr(i)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if n < unsafe.Sizeof(argv) {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			argv += unsafe.Sizeof(argv)
 | 
			
		||||
			n -= unsafe.Sizeof(argv)
 | 
			
		||||
		}
 | 
			
		||||
		execPath = args[0]
 | 
			
		||||
		// There is no canonical way to get an executable path on
 | 
			
		||||
		// OpenBSD, so check PATH in case we are called directly
 | 
			
		||||
		if execPath[0] != '/' && execPath[0] != '.' {
 | 
			
		||||
			execIsInPath, err := exec.LookPath(execPath)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				execPath = execIsInPath
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		for i, v := range buf {
 | 
			
		||||
			if v == 0 {
 | 
			
		||||
				buf = buf[:i]
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		execPath = string(buf)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	// execPath will not be empty due to above checks.
 | 
			
		||||
	// Try to get the absolute path if the execPath is not rooted.
 | 
			
		||||
	if execPath[0] != '/' {
 | 
			
		||||
		execPath, err = getAbs(execPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return execPath, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// For darwin KERN_PROCARGS may return the path to a symlink rather than the
 | 
			
		||||
	// actual executable.
 | 
			
		||||
	if runtime.GOOS == "darwin" {
 | 
			
		||||
		if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
 | 
			
		||||
			return execPath, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return execPath, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAbs(execPath string) (string, error) {
 | 
			
		||||
	if initCwdErr != nil {
 | 
			
		||||
		return execPath, initCwdErr
 | 
			
		||||
	}
 | 
			
		||||
	// The execPath may begin with a "../" or a "./" so clean it first.
 | 
			
		||||
	// Join the two paths, trailing and starting slashes undetermined, so use
 | 
			
		||||
	// the generic Join function.
 | 
			
		||||
	return filepath.Join(initCwd, filepath.Clean(execPath)), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								vendor/github.com/kardianos/osext/osext_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/kardianos/osext/osext_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,36 +0,0 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
//+build !go1.8
 | 
			
		||||
 | 
			
		||||
package osext
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unicode/utf16"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	kernel                = syscall.MustLoadDLL("kernel32.dll")
 | 
			
		||||
	getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetModuleFileName() with hModule = NULL
 | 
			
		||||
func executable() (exePath string, err error) {
 | 
			
		||||
	return getModuleFileName()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getModuleFileName() (string, error) {
 | 
			
		||||
	var n uint32
 | 
			
		||||
	b := make([]uint16, syscall.MAX_PATH)
 | 
			
		||||
	size := uint32(len(b))
 | 
			
		||||
 | 
			
		||||
	r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
 | 
			
		||||
	n = uint32(r0)
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		return "", e1
 | 
			
		||||
	}
 | 
			
		||||
	return string(utf16.Decode(b[0:n])), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -70,7 +70,11 @@ func (a *API) GetConversations(unreadOnly bool) ([]chat1.ConvSummary, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) GetConversation(convID chat1.ConvIDStr) (res chat1.ConvSummary, err error) {
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method":"list", "params": { "options": { "conversation_id": "%s"}}}`, convID)
 | 
			
		||||
	convIDEscaped, err := json.Marshal(convID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return res, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method":"list", "params": { "options": { "conversation_id": %s}}}`, convIDEscaped)
 | 
			
		||||
	output, err := a.doFetch(apiInput)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return res, err
 | 
			
		||||
@@ -94,7 +98,7 @@ func (a *API) GetTextMessages(channel chat1.ChatChannel, unreadOnly bool) ([]cha
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "read", "params": {"options": {"channel": %s}}}`, string(channelBytes))
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "read", "params": {"options": {"channel": %s}}}`, channelBytes)
 | 
			
		||||
	output, err := a.doFetch(apiInput)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -324,7 +328,11 @@ type LeaveChannel struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) ListChannels(teamName string) ([]string, error) {
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "listconvsonname", "params": {"options": {"topic_type": "CHAT", "members_type": "team", "name": "%s"}}}`, teamName)
 | 
			
		||||
	teamNameEscaped, err := json.Marshal(teamName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "listconvsonname", "params": {"options": {"topic_type": "CHAT", "members_type": "team", "name": %s}}}`, teamNameEscaped)
 | 
			
		||||
	output, err := a.doFetch(apiInput)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -347,7 +355,16 @@ func (a *API) ListChannels(teamName string) ([]string, error) {
 | 
			
		||||
func (a *API) JoinChannel(teamName string, channelName string) (chat1.EmptyRes, error) {
 | 
			
		||||
	empty := chat1.EmptyRes{}
 | 
			
		||||
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "join", "params": {"options": {"channel": {"name": "%s", "members_type": "team", "topic_name": "%s"}}}}`, teamName, channelName)
 | 
			
		||||
	teamNameEscaped, err := json.Marshal(teamName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return empty, err
 | 
			
		||||
	}
 | 
			
		||||
	channelNameEscaped, err := json.Marshal(channelName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return empty, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "join", "params": {"options": {"channel": {"name": %s, "members_type": "team", "topic_name": %s}}}}`,
 | 
			
		||||
		teamNameEscaped, channelNameEscaped)
 | 
			
		||||
	output, err := a.doFetch(apiInput)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return empty, err
 | 
			
		||||
@@ -367,7 +384,16 @@ func (a *API) JoinChannel(teamName string, channelName string) (chat1.EmptyRes,
 | 
			
		||||
func (a *API) LeaveChannel(teamName string, channelName string) (chat1.EmptyRes, error) {
 | 
			
		||||
	empty := chat1.EmptyRes{}
 | 
			
		||||
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "leave", "params": {"options": {"channel": {"name": "%s", "members_type": "team", "topic_name": "%s"}}}}`, teamName, channelName)
 | 
			
		||||
	teamNameEscaped, err := json.Marshal(teamName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return empty, err
 | 
			
		||||
	}
 | 
			
		||||
	channelNameEscaped, err := json.Marshal(channelName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return empty, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "leave", "params": {"options": {"channel": {"name": %s, "members_type": "team", "topic_name": %s}}}}`,
 | 
			
		||||
		teamNameEscaped, channelNameEscaped)
 | 
			
		||||
	output, err := a.doFetch(apiInput)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return empty, err
 | 
			
		||||
@@ -461,13 +487,28 @@ func (a *API) AdvertiseCommands(ad Advertisement) (SendResponse, error) {
 | 
			
		||||
	return a.doSend(newAdvertiseCmdsMsgArg(ad))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) ClearCommands() error {
 | 
			
		||||
	arg := struct {
 | 
			
		||||
		Method string
 | 
			
		||||
	}{
 | 
			
		||||
type clearCmdsOptions struct {
 | 
			
		||||
	Filter *chat1.ClearCommandAPIParam `json:"filter"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clearCmdsParams struct {
 | 
			
		||||
	Options clearCmdsOptions `json:"options"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clearCmdsArg struct {
 | 
			
		||||
	Method string          `json:"method"`
 | 
			
		||||
	Params clearCmdsParams `json:"params,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) ClearCommands(filter *chat1.ClearCommandAPIParam) error {
 | 
			
		||||
	_, err := a.doSend(clearCmdsArg{
 | 
			
		||||
		Method: "clearcommands",
 | 
			
		||||
	}
 | 
			
		||||
	_, err := a.doSend(arg)
 | 
			
		||||
		Params: clearCmdsParams{
 | 
			
		||||
			Options: clearCmdsOptions{
 | 
			
		||||
				Filter: filter,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,9 +1,14 @@
 | 
			
		||||
package kbchat
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ErrorCode int
 | 
			
		||||
 | 
			
		||||
var errAPIDisconnected = errors.New("chat API disconnected")
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	RevisionErrorCode          ErrorCode = 2760
 | 
			
		||||
	DeleteNonExistentErrorCode ErrorCode = 2762
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										423
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										423
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -7,7 +7,6 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -18,63 +17,110 @@ import (
 | 
			
		||||
	"github.com/keybase/go-keybase-chat-bot/kbchat/types/stellar1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// API is the main object used for communicating with the Keybase JSON API
 | 
			
		||||
type API struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	apiInput      io.Writer
 | 
			
		||||
	apiOutput     *bufio.Reader
 | 
			
		||||
	apiCmd        *exec.Cmd
 | 
			
		||||
	username      string
 | 
			
		||||
	runOpts       RunOptions
 | 
			
		||||
	subscriptions []*NewSubscription
 | 
			
		||||
// SubscriptionMessage contains a message and conversation object
 | 
			
		||||
type SubscriptionMessage struct {
 | 
			
		||||
	Message      chat1.MsgSummary
 | 
			
		||||
	Conversation chat1.ConvSummary
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUsername(runOpts RunOptions) (username string, err error) {
 | 
			
		||||
	p := runOpts.Command("whoami", "-json")
 | 
			
		||||
	output, err := p.StdoutPipe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	p.ExtraFiles = []*os.File{output.(*os.File)}
 | 
			
		||||
	if err = p.Start(); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
type SubscriptionConversation struct {
 | 
			
		||||
	Conversation chat1.ConvSummary
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	doneCh := make(chan error)
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer func() { close(doneCh) }()
 | 
			
		||||
		statusJSON, err := ioutil.ReadAll(output)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			doneCh <- fmt.Errorf("error reading whoami output: %v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		var status keybase1.CurrentStatus
 | 
			
		||||
		if err := json.Unmarshal(statusJSON, &status); err != nil {
 | 
			
		||||
			doneCh <- fmt.Errorf("invalid whoami JSON %q: %v", statusJSON, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if status.LoggedIn && status.User != nil {
 | 
			
		||||
			username = status.User.Username
 | 
			
		||||
			doneCh <- nil
 | 
			
		||||
		} else {
 | 
			
		||||
			doneCh <- fmt.Errorf("unable to authenticate to keybase service: logged in: %v user: %+v", status.LoggedIn, status.User)
 | 
			
		||||
		}
 | 
			
		||||
		// Cleanup the command
 | 
			
		||||
		if err := p.Wait(); err != nil {
 | 
			
		||||
			log.Printf("unable to wait for cmd: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
type SubscriptionWalletEvent struct {
 | 
			
		||||
	Payment stellar1.PaymentDetailsLocal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Subscription has methods to control the background message fetcher loop
 | 
			
		||||
type Subscription struct {
 | 
			
		||||
	*DebugOutput
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
 | 
			
		||||
	newMsgsCh   chan SubscriptionMessage
 | 
			
		||||
	newConvsCh  chan SubscriptionConversation
 | 
			
		||||
	newWalletCh chan SubscriptionWalletEvent
 | 
			
		||||
	errorCh     chan error
 | 
			
		||||
	running     bool
 | 
			
		||||
	shutdownCh  chan struct{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSubscription() *Subscription {
 | 
			
		||||
	newMsgsCh := make(chan SubscriptionMessage, 100)
 | 
			
		||||
	newConvsCh := make(chan SubscriptionConversation, 100)
 | 
			
		||||
	newWalletCh := make(chan SubscriptionWalletEvent, 100)
 | 
			
		||||
	errorCh := make(chan error, 100)
 | 
			
		||||
	shutdownCh := make(chan struct{})
 | 
			
		||||
	return &Subscription{
 | 
			
		||||
		DebugOutput: NewDebugOutput("Subscription"),
 | 
			
		||||
		newMsgsCh:   newMsgsCh,
 | 
			
		||||
		newConvsCh:  newConvsCh,
 | 
			
		||||
		newWalletCh: newWalletCh,
 | 
			
		||||
		shutdownCh:  shutdownCh,
 | 
			
		||||
		errorCh:     errorCh,
 | 
			
		||||
		running:     true,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read blocks until a new message arrives
 | 
			
		||||
func (m *Subscription) Read() (msg SubscriptionMessage, err error) {
 | 
			
		||||
	defer m.Trace(&err, "Read")()
 | 
			
		||||
	select {
 | 
			
		||||
	case err = <-doneCh:
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	case <-time.After(5 * time.Second):
 | 
			
		||||
		return "", errors.New("unable to run Keybase command")
 | 
			
		||||
	case msg = <-m.newMsgsCh:
 | 
			
		||||
		return msg, nil
 | 
			
		||||
	case err = <-m.errorCh:
 | 
			
		||||
		return SubscriptionMessage{}, err
 | 
			
		||||
	case <-m.shutdownCh:
 | 
			
		||||
		return SubscriptionMessage{}, errors.New("Subscription shutdown")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	return username, nil
 | 
			
		||||
func (m *Subscription) ReadNewConvs() (conv SubscriptionConversation, err error) {
 | 
			
		||||
	defer m.Trace(&err, "ReadNewConvs")()
 | 
			
		||||
	select {
 | 
			
		||||
	case conv = <-m.newConvsCh:
 | 
			
		||||
		return conv, nil
 | 
			
		||||
	case err = <-m.errorCh:
 | 
			
		||||
		return SubscriptionConversation{}, err
 | 
			
		||||
	case <-m.shutdownCh:
 | 
			
		||||
		return SubscriptionConversation{}, errors.New("Subscription shutdown")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read blocks until a new message arrives
 | 
			
		||||
func (m *Subscription) ReadWallet() (msg SubscriptionWalletEvent, err error) {
 | 
			
		||||
	defer m.Trace(&err, "ReadWallet")()
 | 
			
		||||
	select {
 | 
			
		||||
	case msg = <-m.newWalletCh:
 | 
			
		||||
		return msg, nil
 | 
			
		||||
	case err = <-m.errorCh:
 | 
			
		||||
		return SubscriptionWalletEvent{}, err
 | 
			
		||||
	case <-m.shutdownCh:
 | 
			
		||||
		return SubscriptionWalletEvent{}, errors.New("Subscription shutdown")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Shutdown terminates the background process
 | 
			
		||||
func (m *Subscription) Shutdown() {
 | 
			
		||||
	defer m.Trace(nil, "Shutdown")()
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	defer m.Unlock()
 | 
			
		||||
	if m.running {
 | 
			
		||||
		close(m.shutdownCh)
 | 
			
		||||
		m.running = false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ListenOptions struct {
 | 
			
		||||
	Wallet bool
 | 
			
		||||
	Convs  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PaymentHolder struct {
 | 
			
		||||
	Payment stellar1.PaymentDetailsLocal `json:"notification"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TypeHolder struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OneshotOptions struct {
 | 
			
		||||
@@ -110,22 +156,101 @@ func (r RunOptions) Command(args ...string) *exec.Cmd {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start fires up the Keybase JSON API in stdin/stdout mode
 | 
			
		||||
func Start(runOpts RunOptions) (*API, error) {
 | 
			
		||||
	api := &API{
 | 
			
		||||
		runOpts: runOpts,
 | 
			
		||||
	}
 | 
			
		||||
func Start(runOpts RunOptions, opts ...func(*API)) (*API, error) {
 | 
			
		||||
	api := NewAPI(runOpts, opts...)
 | 
			
		||||
	if err := api.startPipes(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return api, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// API is the main object used for communicating with the Keybase JSON API
 | 
			
		||||
type API struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	*DebugOutput
 | 
			
		||||
	apiInput      io.Writer
 | 
			
		||||
	apiOutput     *bufio.Reader
 | 
			
		||||
	apiCmd        *exec.Cmd
 | 
			
		||||
	username      string
 | 
			
		||||
	runOpts       RunOptions
 | 
			
		||||
	subscriptions []*Subscription
 | 
			
		||||
	Timeout       time.Duration
 | 
			
		||||
	LogSendBytes  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CustomTimeout(timeout time.Duration) func(*API) {
 | 
			
		||||
	return func(a *API) {
 | 
			
		||||
		a.Timeout = timeout
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAPI(runOpts RunOptions, opts ...func(*API)) *API {
 | 
			
		||||
	api := &API{
 | 
			
		||||
		DebugOutput:  NewDebugOutput("API"),
 | 
			
		||||
		runOpts:      runOpts,
 | 
			
		||||
		Timeout:      5 * time.Second,
 | 
			
		||||
		LogSendBytes: 1024 * 1024 * 5, // request 5MB so we don't get killed
 | 
			
		||||
	}
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
		opt(api)
 | 
			
		||||
	}
 | 
			
		||||
	return api
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) Command(args ...string) *exec.Cmd {
 | 
			
		||||
	return a.runOpts.Command(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) getUsername(runOpts RunOptions) (username string, err error) {
 | 
			
		||||
	p := runOpts.Command("whoami", "-json")
 | 
			
		||||
	output, err := p.StdoutPipe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	p.ExtraFiles = []*os.File{output.(*os.File)}
 | 
			
		||||
	if err = p.Start(); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	doneCh := make(chan error)
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer func() { close(doneCh) }()
 | 
			
		||||
		statusJSON, err := ioutil.ReadAll(output)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			doneCh <- fmt.Errorf("error reading whoami output: %v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		var status keybase1.CurrentStatus
 | 
			
		||||
		if err := json.Unmarshal(statusJSON, &status); err != nil {
 | 
			
		||||
			doneCh <- fmt.Errorf("invalid whoami JSON %q: %v", statusJSON, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if status.LoggedIn && status.User != nil {
 | 
			
		||||
			username = status.User.Username
 | 
			
		||||
			doneCh <- nil
 | 
			
		||||
		} else {
 | 
			
		||||
			doneCh <- fmt.Errorf("unable to authenticate to keybase service: logged in: %v user: %+v", status.LoggedIn, status.User)
 | 
			
		||||
		}
 | 
			
		||||
		// Cleanup the command
 | 
			
		||||
		if err := p.Wait(); err != nil {
 | 
			
		||||
			a.Debug("unable to wait for cmd: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	select {
 | 
			
		||||
	case err = <-doneCh:
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	case <-time.After(a.Timeout):
 | 
			
		||||
		return "", errors.New("unable to run Keybase command")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return username, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) auth() (string, error) {
 | 
			
		||||
	username, err := getUsername(a.runOpts)
 | 
			
		||||
	username, err := a.getUsername(a.runOpts)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return username, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -194,8 +319,6 @@ func (a *API) startPipes() (err error) {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errAPIDisconnected = errors.New("chat API disconnected")
 | 
			
		||||
 | 
			
		||||
func (a *API) getAPIPipesLocked() (io.Writer, *bufio.Reader, error) {
 | 
			
		||||
	// this should only be called inside a lock
 | 
			
		||||
	if a.apiCmd == nil {
 | 
			
		||||
@@ -214,7 +337,7 @@ func (a *API) doSend(arg interface{}) (resp SendResponse, err error) {
 | 
			
		||||
 | 
			
		||||
	bArg, err := json.Marshal(arg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return SendResponse{}, err
 | 
			
		||||
		return SendResponse{}, fmt.Errorf("unable to send arg: %+v: %v", arg, err)
 | 
			
		||||
	}
 | 
			
		||||
	input, output, err := a.getAPIPipesLocked()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -228,7 +351,7 @@ func (a *API) doSend(arg interface{}) (resp SendResponse, err error) {
 | 
			
		||||
		return SendResponse{}, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := json.Unmarshal(responseRaw, &resp); err != nil {
 | 
			
		||||
		return resp, fmt.Errorf("failed to decode API response: %s", err)
 | 
			
		||||
		return resp, fmt.Errorf("failed to decode API response: %v %v", responseRaw, err)
 | 
			
		||||
	} else if resp.Error != nil {
 | 
			
		||||
		return resp, errors.New(resp.Error.Message)
 | 
			
		||||
	}
 | 
			
		||||
@@ -254,97 +377,13 @@ func (a *API) doFetch(apiInput string) ([]byte, error) {
 | 
			
		||||
	return byteOutput, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SubscriptionMessage contains a message and conversation object
 | 
			
		||||
type SubscriptionMessage struct {
 | 
			
		||||
	Message      chat1.MsgSummary
 | 
			
		||||
	Conversation chat1.ConvSummary
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SubscriptionConversation struct {
 | 
			
		||||
	Conversation chat1.ConvSummary
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SubscriptionWalletEvent struct {
 | 
			
		||||
	Payment stellar1.PaymentDetailsLocal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSubscription has methods to control the background message fetcher loop
 | 
			
		||||
type NewSubscription struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
 | 
			
		||||
	newMsgsCh   <-chan SubscriptionMessage
 | 
			
		||||
	newConvsCh  <-chan SubscriptionConversation
 | 
			
		||||
	newWalletCh <-chan SubscriptionWalletEvent
 | 
			
		||||
	errorCh     <-chan error
 | 
			
		||||
	running     bool
 | 
			
		||||
	shutdownCh  chan struct{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read blocks until a new message arrives
 | 
			
		||||
func (m *NewSubscription) Read() (SubscriptionMessage, error) {
 | 
			
		||||
	select {
 | 
			
		||||
	case msg := <-m.newMsgsCh:
 | 
			
		||||
		return msg, nil
 | 
			
		||||
	case err := <-m.errorCh:
 | 
			
		||||
		return SubscriptionMessage{}, err
 | 
			
		||||
	case <-m.shutdownCh:
 | 
			
		||||
		return SubscriptionMessage{}, errors.New("Subscription shutdown")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *NewSubscription) ReadNewConvs() (SubscriptionConversation, error) {
 | 
			
		||||
	select {
 | 
			
		||||
	case conv := <-m.newConvsCh:
 | 
			
		||||
		return conv, nil
 | 
			
		||||
	case err := <-m.errorCh:
 | 
			
		||||
		return SubscriptionConversation{}, err
 | 
			
		||||
	case <-m.shutdownCh:
 | 
			
		||||
		return SubscriptionConversation{}, errors.New("Subscription shutdown")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read blocks until a new message arrives
 | 
			
		||||
func (m *NewSubscription) ReadWallet() (SubscriptionWalletEvent, error) {
 | 
			
		||||
	select {
 | 
			
		||||
	case msg := <-m.newWalletCh:
 | 
			
		||||
		return msg, nil
 | 
			
		||||
	case err := <-m.errorCh:
 | 
			
		||||
		return SubscriptionWalletEvent{}, err
 | 
			
		||||
	case <-m.shutdownCh:
 | 
			
		||||
		return SubscriptionWalletEvent{}, errors.New("Subscription shutdown")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Shutdown terminates the background process
 | 
			
		||||
func (m *NewSubscription) Shutdown() {
 | 
			
		||||
	m.Lock()
 | 
			
		||||
	defer m.Unlock()
 | 
			
		||||
	if m.running {
 | 
			
		||||
		close(m.shutdownCh)
 | 
			
		||||
		m.running = false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ListenOptions struct {
 | 
			
		||||
	Wallet bool
 | 
			
		||||
	Convs  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PaymentHolder struct {
 | 
			
		||||
	Payment stellar1.PaymentDetailsLocal `json:"notification"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TypeHolder struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenForNewTextMessages proxies to Listen without wallet events
 | 
			
		||||
func (a *API) ListenForNewTextMessages() (*NewSubscription, error) {
 | 
			
		||||
func (a *API) ListenForNewTextMessages() (*Subscription, error) {
 | 
			
		||||
	opts := ListenOptions{Wallet: false}
 | 
			
		||||
	return a.Listen(opts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) registerSubscription(sub *NewSubscription) {
 | 
			
		||||
func (a *API) registerSubscription(sub *Subscription) {
 | 
			
		||||
	a.Lock()
 | 
			
		||||
	defer a.Unlock()
 | 
			
		||||
	a.subscriptions = append(a.subscriptions, sub)
 | 
			
		||||
@@ -352,30 +391,17 @@ func (a *API) registerSubscription(sub *NewSubscription) {
 | 
			
		||||
 | 
			
		||||
// Listen fires of a background loop and puts chat messages and wallet
 | 
			
		||||
// events into channels
 | 
			
		||||
func (a *API) Listen(opts ListenOptions) (*NewSubscription, error) {
 | 
			
		||||
	newMsgsCh := make(chan SubscriptionMessage, 100)
 | 
			
		||||
	newConvsCh := make(chan SubscriptionConversation, 100)
 | 
			
		||||
	newWalletCh := make(chan SubscriptionWalletEvent, 100)
 | 
			
		||||
	errorCh := make(chan error, 100)
 | 
			
		||||
	shutdownCh := make(chan struct{})
 | 
			
		||||
func (a *API) Listen(opts ListenOptions) (*Subscription, error) {
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	sub := &NewSubscription{
 | 
			
		||||
		newMsgsCh:   newMsgsCh,
 | 
			
		||||
		newConvsCh:  newConvsCh,
 | 
			
		||||
		newWalletCh: newWalletCh,
 | 
			
		||||
		shutdownCh:  shutdownCh,
 | 
			
		||||
		errorCh:     errorCh,
 | 
			
		||||
		running:     true,
 | 
			
		||||
	}
 | 
			
		||||
	sub := NewSubscription()
 | 
			
		||||
	a.registerSubscription(sub)
 | 
			
		||||
	pause := 2 * time.Second
 | 
			
		||||
	readScanner := func(boutput *bufio.Scanner) {
 | 
			
		||||
		defer func() { done <- struct{}{} }()
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-shutdownCh:
 | 
			
		||||
				log.Printf("readScanner: received shutdown")
 | 
			
		||||
			case <-sub.shutdownCh:
 | 
			
		||||
				a.Debug("readScanner: received shutdown")
 | 
			
		||||
				return
 | 
			
		||||
			default:
 | 
			
		||||
			}
 | 
			
		||||
@@ -383,18 +409,18 @@ func (a *API) Listen(opts ListenOptions) (*NewSubscription, error) {
 | 
			
		||||
			t := boutput.Text()
 | 
			
		||||
			var typeHolder TypeHolder
 | 
			
		||||
			if err := json.Unmarshal([]byte(t), &typeHolder); err != nil {
 | 
			
		||||
				errorCh <- err
 | 
			
		||||
				sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			switch typeHolder.Type {
 | 
			
		||||
			case "chat":
 | 
			
		||||
				var notification chat1.MsgNotification
 | 
			
		||||
				if err := json.Unmarshal([]byte(t), ¬ification); err != nil {
 | 
			
		||||
					errorCh <- err
 | 
			
		||||
					sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				if notification.Error != nil {
 | 
			
		||||
					log.Printf("error message received: %s", *notification.Error)
 | 
			
		||||
					a.Debug("error message received: %s", *notification.Error)
 | 
			
		||||
				} else if notification.Msg != nil {
 | 
			
		||||
					subscriptionMessage := SubscriptionMessage{
 | 
			
		||||
						Message: *notification.Msg,
 | 
			
		||||
@@ -403,30 +429,30 @@ func (a *API) Listen(opts ListenOptions) (*NewSubscription, error) {
 | 
			
		||||
							Channel: notification.Msg.Channel,
 | 
			
		||||
						},
 | 
			
		||||
					}
 | 
			
		||||
					newMsgsCh <- subscriptionMessage
 | 
			
		||||
					sub.newMsgsCh <- subscriptionMessage
 | 
			
		||||
				}
 | 
			
		||||
			case "chat_conv":
 | 
			
		||||
				var notification chat1.ConvNotification
 | 
			
		||||
				if err := json.Unmarshal([]byte(t), ¬ification); err != nil {
 | 
			
		||||
					errorCh <- err
 | 
			
		||||
					sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				if notification.Error != nil {
 | 
			
		||||
					log.Printf("error message received: %s", *notification.Error)
 | 
			
		||||
					a.Debug("error message received: %s", *notification.Error)
 | 
			
		||||
				} else if notification.Conv != nil {
 | 
			
		||||
					subscriptionConv := SubscriptionConversation{
 | 
			
		||||
						Conversation: *notification.Conv,
 | 
			
		||||
					}
 | 
			
		||||
					newConvsCh <- subscriptionConv
 | 
			
		||||
					sub.newConvsCh <- subscriptionConv
 | 
			
		||||
				}
 | 
			
		||||
			case "wallet":
 | 
			
		||||
				var holder PaymentHolder
 | 
			
		||||
				if err := json.Unmarshal([]byte(t), &holder); err != nil {
 | 
			
		||||
					errorCh <- err
 | 
			
		||||
					sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				subscriptionPayment := SubscriptionWalletEvent(holder)
 | 
			
		||||
				newWalletCh <- subscriptionPayment
 | 
			
		||||
				sub.newWalletCh <- subscriptionPayment
 | 
			
		||||
			default:
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
@@ -434,31 +460,31 @@ func (a *API) Listen(opts ListenOptions) (*NewSubscription, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	attempts := 0
 | 
			
		||||
	maxAttempts := 1800
 | 
			
		||||
	maxAttempts := 30
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			close(newMsgsCh)
 | 
			
		||||
			close(newConvsCh)
 | 
			
		||||
			close(newWalletCh)
 | 
			
		||||
			close(errorCh)
 | 
			
		||||
			close(sub.newMsgsCh)
 | 
			
		||||
			close(sub.newConvsCh)
 | 
			
		||||
			close(sub.newWalletCh)
 | 
			
		||||
			close(sub.errorCh)
 | 
			
		||||
		}()
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-shutdownCh:
 | 
			
		||||
				log.Printf("Listen: received shutdown")
 | 
			
		||||
			case <-sub.shutdownCh:
 | 
			
		||||
				a.Debug("Listen: received shutdown")
 | 
			
		||||
				return
 | 
			
		||||
			default:
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if attempts >= maxAttempts {
 | 
			
		||||
				if err := a.LogSend("Listen: failed to auth, giving up"); err != nil {
 | 
			
		||||
					log.Printf("Listen: logsend failed to send: %v", err)
 | 
			
		||||
					a.Debug("Listen: logsend failed to send: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				panic("Listen: failed to auth, giving up")
 | 
			
		||||
			}
 | 
			
		||||
			attempts++
 | 
			
		||||
			if _, err := a.auth(); err != nil {
 | 
			
		||||
				log.Printf("Listen: failed to auth: %s", err)
 | 
			
		||||
				a.Debug("Listen: failed to auth: %s", err)
 | 
			
		||||
				time.Sleep(pause)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
@@ -472,13 +498,13 @@ func (a *API) Listen(opts ListenOptions) (*NewSubscription, error) {
 | 
			
		||||
			p := a.runOpts.Command(cmdElements...)
 | 
			
		||||
			output, err := p.StdoutPipe()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Listen: failed to listen: %s", err)
 | 
			
		||||
				a.Debug("Listen: failed to listen: %s", err)
 | 
			
		||||
				time.Sleep(pause)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			stderr, err := p.StderrPipe()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Listen: failed to listen to stderr: %s", err)
 | 
			
		||||
				a.Debug("Listen: failed to listen to stderr: %s", err)
 | 
			
		||||
				time.Sleep(pause)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
@@ -486,19 +512,27 @@ func (a *API) Listen(opts ListenOptions) (*NewSubscription, error) {
 | 
			
		||||
			boutput := bufio.NewScanner(output)
 | 
			
		||||
			if err := p.Start(); err != nil {
 | 
			
		||||
 | 
			
		||||
				log.Printf("Listen: failed to make listen scanner: %s", err)
 | 
			
		||||
				a.Debug("Listen: failed to make listen scanner: %s", err)
 | 
			
		||||
				time.Sleep(pause)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			attempts = 0
 | 
			
		||||
			go readScanner(boutput)
 | 
			
		||||
			<-done
 | 
			
		||||
			select {
 | 
			
		||||
			case <-sub.shutdownCh:
 | 
			
		||||
				a.Debug("Listen: received shutdown")
 | 
			
		||||
				return
 | 
			
		||||
			case <-done:
 | 
			
		||||
			}
 | 
			
		||||
			if err := p.Wait(); err != nil {
 | 
			
		||||
				stderrBytes, rerr := ioutil.ReadAll(stderr)
 | 
			
		||||
				if rerr != nil {
 | 
			
		||||
					stderrBytes = []byte("failed to get stderr")
 | 
			
		||||
					stderrBytes = []byte(fmt.Sprintf("failed to get stderr: %v", rerr))
 | 
			
		||||
				}
 | 
			
		||||
				a.Debug("Listen: failed to Wait for command, restarting pipes: %s (```%s```)", err, stderrBytes)
 | 
			
		||||
				if err := a.startPipes(); err != nil {
 | 
			
		||||
					a.Debug("Listen: failed to restart pipes: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				log.Printf("Listen: failed to Wait for command: %s (```%s```)", err, stderrBytes)
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(pause)
 | 
			
		||||
		}
 | 
			
		||||
@@ -515,31 +549,27 @@ func (a *API) LogSend(feedback string) error {
 | 
			
		||||
		"log", "send",
 | 
			
		||||
		"--no-confirm",
 | 
			
		||||
		"--feedback", feedback,
 | 
			
		||||
		"-n", fmt.Sprintf("%d", a.LogSendBytes),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We're determining whether the service is already running by running status
 | 
			
		||||
	// with autofork disabled.
 | 
			
		||||
	if err := a.runOpts.Command("--no-auto-fork", "status"); err != nil {
 | 
			
		||||
		// Assume that there's no service running, so log send as standalone
 | 
			
		||||
		args = append([]string{"--standalone"}, args...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return a.runOpts.Command(args...).Run()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) Shutdown() error {
 | 
			
		||||
func (a *API) Shutdown() (err error) {
 | 
			
		||||
	defer a.Trace(&err, "Shutdown")()
 | 
			
		||||
	a.Lock()
 | 
			
		||||
	defer a.Unlock()
 | 
			
		||||
	for _, sub := range a.subscriptions {
 | 
			
		||||
		sub.Shutdown()
 | 
			
		||||
	}
 | 
			
		||||
	if a.apiCmd != nil {
 | 
			
		||||
		a.Debug("waiting for API command")
 | 
			
		||||
		if err := a.apiCmd.Wait(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.runOpts.Oneshot != nil {
 | 
			
		||||
		a.Debug("logging out")
 | 
			
		||||
		err := a.runOpts.Command("logout", "--force").Run()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
@@ -547,6 +577,7 @@ func (a *API) Shutdown() error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.runOpts.StartService {
 | 
			
		||||
		a.Debug("stopping service")
 | 
			
		||||
		err := a.runOpts.Command("ctl", "stop", "--shutdown").Run()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/team.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -26,7 +26,11 @@ type ListUserMemberships struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) ListMembersOfTeam(teamName string) (res keybase1.TeamMembersDetails, err error) {
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "list-team-memberships", "params": {"options": {"team": "%s"}}}`, teamName)
 | 
			
		||||
	teamNameEscaped, err := json.Marshal(teamName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return res, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "list-team-memberships", "params": {"options": {"team": %s}}}`, teamNameEscaped)
 | 
			
		||||
	cmd := a.runOpts.Command("team", "api")
 | 
			
		||||
	cmd.Stdin = strings.NewReader(apiInput)
 | 
			
		||||
	var stderr bytes.Buffer
 | 
			
		||||
@@ -51,7 +55,11 @@ func (a *API) ListMembersOfTeam(teamName string) (res keybase1.TeamMembersDetail
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *API) ListUserMemberships(username string) ([]keybase1.AnnotatedMemberInfo, error) {
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "list-user-memberships", "params": {"options": {"username": "%s"}}}`, username)
 | 
			
		||||
	usernameEscaped, err := json.Marshal(username)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	apiInput := fmt.Sprintf(`{"method": "list-user-memberships", "params": {"options": {"username": %s}}}`, usernameEscaped)
 | 
			
		||||
	cmd := a.runOpts.Command("team", "api")
 | 
			
		||||
	cmd.Stdin = strings.NewReader(apiInput)
 | 
			
		||||
	var stderr bytes.Buffer
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/api.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										146
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/api.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.6 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.8 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
//   Input file: ../client/protocol/avdl/chat1/api.avdl
 | 
			
		||||
 | 
			
		||||
package chat1
 | 
			
		||||
@@ -139,9 +139,119 @@ func (o MsgFlipContent) DeepCopy() MsgFlipContent {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EmojiContent struct {
 | 
			
		||||
	Alias       string     `codec:"alias" json:"alias"`
 | 
			
		||||
	IsCrossTeam bool       `codec:"isCrossTeam" json:"isCrossTeam"`
 | 
			
		||||
	ConvID      *ConvIDStr `codec:"convID,omitempty" json:"convID,omitempty"`
 | 
			
		||||
	MessageID   *MessageID `codec:"messageID,omitempty" json:"messageID,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o EmojiContent) DeepCopy() EmojiContent {
 | 
			
		||||
	return EmojiContent{
 | 
			
		||||
		Alias:       o.Alias,
 | 
			
		||||
		IsCrossTeam: o.IsCrossTeam,
 | 
			
		||||
		ConvID: (func(x *ConvIDStr) *ConvIDStr {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.ConvID),
 | 
			
		||||
		MessageID: (func(x *MessageID) *MessageID {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.MessageID),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MsgTextContent struct {
 | 
			
		||||
	Body         string             `codec:"body" json:"body"`
 | 
			
		||||
	Payments     []TextPayment      `codec:"payments" json:"payments"`
 | 
			
		||||
	ReplyTo      *MessageID         `codec:"replyTo,omitempty" json:"replyTo,omitempty"`
 | 
			
		||||
	ReplyToUID   *string            `codec:"replyToUID,omitempty" json:"replyToUID,omitempty"`
 | 
			
		||||
	UserMentions []KnownUserMention `codec:"userMentions" json:"userMentions"`
 | 
			
		||||
	TeamMentions []KnownTeamMention `codec:"teamMentions" json:"teamMentions"`
 | 
			
		||||
	LiveLocation *LiveLocation      `codec:"liveLocation,omitempty" json:"liveLocation,omitempty"`
 | 
			
		||||
	Emojis       []EmojiContent     `codec:"emojis" json:"emojis"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o MsgTextContent) DeepCopy() MsgTextContent {
 | 
			
		||||
	return MsgTextContent{
 | 
			
		||||
		Body: o.Body,
 | 
			
		||||
		Payments: (func(x []TextPayment) []TextPayment {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make([]TextPayment, len(x))
 | 
			
		||||
			for i, v := range x {
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[i] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Payments),
 | 
			
		||||
		ReplyTo: (func(x *MessageID) *MessageID {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.ReplyTo),
 | 
			
		||||
		ReplyToUID: (func(x *string) *string {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			tmp := (*x)
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.ReplyToUID),
 | 
			
		||||
		UserMentions: (func(x []KnownUserMention) []KnownUserMention {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make([]KnownUserMention, len(x))
 | 
			
		||||
			for i, v := range x {
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[i] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.UserMentions),
 | 
			
		||||
		TeamMentions: (func(x []KnownTeamMention) []KnownTeamMention {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make([]KnownTeamMention, len(x))
 | 
			
		||||
			for i, v := range x {
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[i] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.TeamMentions),
 | 
			
		||||
		LiveLocation: (func(x *LiveLocation) *LiveLocation {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.LiveLocation),
 | 
			
		||||
		Emojis: (func(x []EmojiContent) []EmojiContent {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make([]EmojiContent, len(x))
 | 
			
		||||
			for i, v := range x {
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[i] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Emojis),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MsgContent struct {
 | 
			
		||||
	TypeName           string                       `codec:"typeName" json:"type"`
 | 
			
		||||
	Text               *MessageText                 `codec:"text,omitempty" json:"text,omitempty"`
 | 
			
		||||
	Text               *MsgTextContent              `codec:"text,omitempty" json:"text,omitempty"`
 | 
			
		||||
	Attachment         *MessageAttachment           `codec:"attachment,omitempty" json:"attachment,omitempty"`
 | 
			
		||||
	Edit               *MessageEdit                 `codec:"edit,omitempty" json:"edit,omitempty"`
 | 
			
		||||
	Reaction           *MessageReaction             `codec:"reaction,omitempty" json:"reaction,omitempty"`
 | 
			
		||||
@@ -159,7 +269,7 @@ type MsgContent struct {
 | 
			
		||||
func (o MsgContent) DeepCopy() MsgContent {
 | 
			
		||||
	return MsgContent{
 | 
			
		||||
		TypeName: o.TypeName,
 | 
			
		||||
		Text: (func(x *MessageText) *MessageText {
 | 
			
		||||
		Text: (func(x *MsgTextContent) *MsgTextContent {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
@@ -269,7 +379,7 @@ type MsgSummary struct {
 | 
			
		||||
	IsEphemeral         bool                     `codec:"isEphemeral,omitempty" json:"is_ephemeral,omitempty"`
 | 
			
		||||
	IsEphemeralExpired  bool                     `codec:"isEphemeralExpired,omitempty" json:"is_ephemeral_expired,omitempty"`
 | 
			
		||||
	ETime               gregor1.Time             `codec:"eTime,omitempty" json:"e_time,omitempty"`
 | 
			
		||||
	Reactions           *ReactionMap             `codec:"reactions,omitempty" json:"reactions,omitempty"`
 | 
			
		||||
	Reactions           *UIReactionMap           `codec:"reactions,omitempty" json:"reactions,omitempty"`
 | 
			
		||||
	HasPairwiseMacs     bool                     `codec:"hasPairwiseMacs,omitempty" json:"has_pairwise_macs,omitempty"`
 | 
			
		||||
	AtMentionUsernames  []string                 `codec:"atMentionUsernames,omitempty" json:"at_mention_usernames,omitempty"`
 | 
			
		||||
	ChannelMention      string                   `codec:"channelMention,omitempty" json:"channel_mention,omitempty"`
 | 
			
		||||
@@ -304,7 +414,7 @@ func (o MsgSummary) DeepCopy() MsgSummary {
 | 
			
		||||
		IsEphemeral:        o.IsEphemeral,
 | 
			
		||||
		IsEphemeralExpired: o.IsEphemeralExpired,
 | 
			
		||||
		ETime:              o.ETime.DeepCopy(),
 | 
			
		||||
		Reactions: (func(x *ReactionMap) *ReactionMap {
 | 
			
		||||
		Reactions: (func(x *UIReactionMap) *UIReactionMap {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
@@ -832,6 +942,7 @@ type AdvertiseCommandAPIParam struct {
 | 
			
		||||
	Typ      string                `codec:"typ" json:"type"`
 | 
			
		||||
	Commands []UserBotCommandInput `codec:"commands" json:"commands"`
 | 
			
		||||
	TeamName string                `codec:"teamName,omitempty" json:"team_name,omitempty"`
 | 
			
		||||
	ConvID   ConvIDStr             `codec:"convID,omitempty" json:"conv_id,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o AdvertiseCommandAPIParam) DeepCopy() AdvertiseCommandAPIParam {
 | 
			
		||||
@@ -849,6 +960,21 @@ func (o AdvertiseCommandAPIParam) DeepCopy() AdvertiseCommandAPIParam {
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Commands),
 | 
			
		||||
		TeamName: o.TeamName,
 | 
			
		||||
		ConvID:   o.ConvID.DeepCopy(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ClearCommandAPIParam struct {
 | 
			
		||||
	Typ      string    `codec:"typ" json:"type"`
 | 
			
		||||
	TeamName string    `codec:"teamName,omitempty" json:"team_name,omitempty"`
 | 
			
		||||
	ConvID   ConvIDStr `codec:"convID,omitempty" json:"conv_id,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o ClearCommandAPIParam) DeepCopy() ClearCommandAPIParam {
 | 
			
		||||
	return ClearCommandAPIParam{
 | 
			
		||||
		Typ:      o.Typ,
 | 
			
		||||
		TeamName: o.TeamName,
 | 
			
		||||
		ConvID:   o.ConvID.DeepCopy(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -897,17 +1023,17 @@ func (o GetResetConvMembersRes) DeepCopy() GetResetConvMembersRes {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DeviceInfo struct {
 | 
			
		||||
	DeviceID          keybase1.DeviceID `codec:"deviceID" json:"id"`
 | 
			
		||||
	DeviceDescription string            `codec:"deviceDescription" json:"description"`
 | 
			
		||||
	DeviceType        string            `codec:"deviceType" json:"type"`
 | 
			
		||||
	DeviceCtime       int64             `codec:"deviceCtime" json:"ctime"`
 | 
			
		||||
	DeviceID          keybase1.DeviceID     `codec:"deviceID" json:"id"`
 | 
			
		||||
	DeviceDescription string                `codec:"deviceDescription" json:"description"`
 | 
			
		||||
	DeviceType        keybase1.DeviceTypeV2 `codec:"deviceType" json:"type"`
 | 
			
		||||
	DeviceCtime       int64                 `codec:"deviceCtime" json:"ctime"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o DeviceInfo) DeepCopy() DeviceInfo {
 | 
			
		||||
	return DeviceInfo{
 | 
			
		||||
		DeviceID:          o.DeviceID.DeepCopy(),
 | 
			
		||||
		DeviceDescription: o.DeviceDescription,
 | 
			
		||||
		DeviceType:        o.DeviceType,
 | 
			
		||||
		DeviceType:        o.DeviceType.DeepCopy(),
 | 
			
		||||
		DeviceCtime:       o.DeviceCtime,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/blocking.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/blocking.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.6 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.8 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
//   Input file: ../client/protocol/avdl/chat1/blocking.avdl
 | 
			
		||||
 | 
			
		||||
package chat1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/chat_ui.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										137
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/chat_ui.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.6 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.8 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
//   Input file: ../client/protocol/avdl/chat1/chat_ui.avdl
 | 
			
		||||
 | 
			
		||||
package chat1
 | 
			
		||||
@@ -537,6 +537,7 @@ type InboxUIItem struct {
 | 
			
		||||
	IsDefaultConv     bool                          `codec:"isDefaultConv" json:"isDefaultConv"`
 | 
			
		||||
	Name              string                        `codec:"name" json:"name"`
 | 
			
		||||
	Snippet           string                        `codec:"snippet" json:"snippet"`
 | 
			
		||||
	SnippetDecorated  string                        `codec:"snippetDecorated" json:"snippetDecorated"`
 | 
			
		||||
	SnippetDecoration SnippetDecoration             `codec:"snippetDecoration" json:"snippetDecoration"`
 | 
			
		||||
	Channel           string                        `codec:"channel" json:"channel"`
 | 
			
		||||
	Headline          string                        `codec:"headline" json:"headline"`
 | 
			
		||||
@@ -579,6 +580,7 @@ func (o InboxUIItem) DeepCopy() InboxUIItem {
 | 
			
		||||
		IsDefaultConv:     o.IsDefaultConv,
 | 
			
		||||
		Name:              o.Name,
 | 
			
		||||
		Snippet:           o.Snippet,
 | 
			
		||||
		SnippetDecorated:  o.SnippetDecorated,
 | 
			
		||||
		SnippetDecoration: o.SnippetDecoration.DeepCopy(),
 | 
			
		||||
		Channel:           o.Channel,
 | 
			
		||||
		Headline:          o.Headline,
 | 
			
		||||
@@ -889,6 +891,50 @@ func (o UIMessageUnfurlInfo) DeepCopy() UIMessageUnfurlInfo {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIReactionDesc struct {
 | 
			
		||||
	Decorated string              `codec:"decorated" json:"decorated"`
 | 
			
		||||
	Users     map[string]Reaction `codec:"users" json:"users"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o UIReactionDesc) DeepCopy() UIReactionDesc {
 | 
			
		||||
	return UIReactionDesc{
 | 
			
		||||
		Decorated: o.Decorated,
 | 
			
		||||
		Users: (func(x map[string]Reaction) map[string]Reaction {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make(map[string]Reaction, len(x))
 | 
			
		||||
			for k, v := range x {
 | 
			
		||||
				kCopy := k
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[kCopy] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Users),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIReactionMap struct {
 | 
			
		||||
	Reactions map[string]UIReactionDesc `codec:"reactions" json:"reactions"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o UIReactionMap) DeepCopy() UIReactionMap {
 | 
			
		||||
	return UIReactionMap{
 | 
			
		||||
		Reactions: (func(x map[string]UIReactionDesc) map[string]UIReactionDesc {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make(map[string]UIReactionDesc, len(x))
 | 
			
		||||
			for k, v := range x {
 | 
			
		||||
				kCopy := k
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[kCopy] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Reactions),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIMessageValid struct {
 | 
			
		||||
	MessageID             MessageID              `codec:"messageID" json:"messageID"`
 | 
			
		||||
	Ctime                 gregor1.Time           `codec:"ctime" json:"ctime"`
 | 
			
		||||
@@ -898,7 +944,7 @@ type UIMessageValid struct {
 | 
			
		||||
	BodySummary           string                 `codec:"bodySummary" json:"bodySummary"`
 | 
			
		||||
	SenderUsername        string                 `codec:"senderUsername" json:"senderUsername"`
 | 
			
		||||
	SenderDeviceName      string                 `codec:"senderDeviceName" json:"senderDeviceName"`
 | 
			
		||||
	SenderDeviceType      string                 `codec:"senderDeviceType" json:"senderDeviceType"`
 | 
			
		||||
	SenderDeviceType      keybase1.DeviceTypeV2  `codec:"senderDeviceType" json:"senderDeviceType"`
 | 
			
		||||
	SenderUID             gregor1.UID            `codec:"senderUID" json:"senderUID"`
 | 
			
		||||
	SenderDeviceID        gregor1.DeviceID       `codec:"senderDeviceID" json:"senderDeviceID"`
 | 
			
		||||
	Superseded            bool                   `codec:"superseded" json:"superseded"`
 | 
			
		||||
@@ -911,7 +957,7 @@ type UIMessageValid struct {
 | 
			
		||||
	IsEphemeralExpired    bool                   `codec:"isEphemeralExpired" json:"isEphemeralExpired"`
 | 
			
		||||
	ExplodedBy            *string                `codec:"explodedBy,omitempty" json:"explodedBy,omitempty"`
 | 
			
		||||
	Etime                 gregor1.Time           `codec:"etime" json:"etime"`
 | 
			
		||||
	Reactions             ReactionMap            `codec:"reactions" json:"reactions"`
 | 
			
		||||
	Reactions             UIReactionMap          `codec:"reactions" json:"reactions"`
 | 
			
		||||
	HasPairwiseMacs       bool                   `codec:"hasPairwiseMacs" json:"hasPairwiseMacs"`
 | 
			
		||||
	PaymentInfos          []UIPaymentInfo        `codec:"paymentInfos" json:"paymentInfos"`
 | 
			
		||||
	RequestInfo           *UIRequestInfo         `codec:"requestInfo,omitempty" json:"requestInfo,omitempty"`
 | 
			
		||||
@@ -947,7 +993,7 @@ func (o UIMessageValid) DeepCopy() UIMessageValid {
 | 
			
		||||
		BodySummary:      o.BodySummary,
 | 
			
		||||
		SenderUsername:   o.SenderUsername,
 | 
			
		||||
		SenderDeviceName: o.SenderDeviceName,
 | 
			
		||||
		SenderDeviceType: o.SenderDeviceType,
 | 
			
		||||
		SenderDeviceType: o.SenderDeviceType.DeepCopy(),
 | 
			
		||||
		SenderUID:        o.SenderUID.DeepCopy(),
 | 
			
		||||
		SenderDeviceID:   o.SenderDeviceID.DeepCopy(),
 | 
			
		||||
		Superseded:       o.Superseded,
 | 
			
		||||
@@ -1068,6 +1114,7 @@ type UIMessageOutbox struct {
 | 
			
		||||
	IsEphemeral       bool            `codec:"isEphemeral" json:"isEphemeral"`
 | 
			
		||||
	FlipGameID        *FlipGameIDStr  `codec:"flipGameID,omitempty" json:"flipGameID,omitempty"`
 | 
			
		||||
	ReplyTo           *UIMessage      `codec:"replyTo,omitempty" json:"replyTo,omitempty"`
 | 
			
		||||
	Supersedes        MessageID       `codec:"supersedes" json:"supersedes"`
 | 
			
		||||
	Filename          string          `codec:"filename" json:"filename"`
 | 
			
		||||
	Title             string          `codec:"title" json:"title"`
 | 
			
		||||
	Preview           *MakePreviewRes `codec:"preview,omitempty" json:"preview,omitempty"`
 | 
			
		||||
@@ -1103,8 +1150,9 @@ func (o UIMessageOutbox) DeepCopy() UIMessageOutbox {
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.ReplyTo),
 | 
			
		||||
		Filename: o.Filename,
 | 
			
		||||
		Title:    o.Title,
 | 
			
		||||
		Supersedes: o.Supersedes.DeepCopy(),
 | 
			
		||||
		Filename:   o.Filename,
 | 
			
		||||
		Title:      o.Title,
 | 
			
		||||
		Preview: (func(x *MakePreviewRes) *MakePreviewRes {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
@@ -1418,6 +1466,7 @@ const (
 | 
			
		||||
	UITextDecorationTyp_LINK               UITextDecorationTyp = 4
 | 
			
		||||
	UITextDecorationTyp_MAILTO             UITextDecorationTyp = 5
 | 
			
		||||
	UITextDecorationTyp_KBFSPATH           UITextDecorationTyp = 6
 | 
			
		||||
	UITextDecorationTyp_EMOJI              UITextDecorationTyp = 7
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (o UITextDecorationTyp) DeepCopy() UITextDecorationTyp { return o }
 | 
			
		||||
@@ -1430,6 +1479,7 @@ var UITextDecorationTypMap = map[string]UITextDecorationTyp{
 | 
			
		||||
	"LINK":               4,
 | 
			
		||||
	"MAILTO":             5,
 | 
			
		||||
	"KBFSPATH":           6,
 | 
			
		||||
	"EMOJI":              7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var UITextDecorationTypRevMap = map[UITextDecorationTyp]string{
 | 
			
		||||
@@ -1440,6 +1490,7 @@ var UITextDecorationTypRevMap = map[UITextDecorationTyp]string{
 | 
			
		||||
	4: "LINK",
 | 
			
		||||
	5: "MAILTO",
 | 
			
		||||
	6: "KBFSPATH",
 | 
			
		||||
	7: "EMOJI",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e UITextDecorationTyp) String() string {
 | 
			
		||||
@@ -1566,6 +1617,7 @@ type UITextDecoration struct {
 | 
			
		||||
	Link__               *UILinkDecoration     `codec:"link,omitempty" json:"link,omitempty"`
 | 
			
		||||
	Mailto__             *UILinkDecoration     `codec:"mailto,omitempty" json:"mailto,omitempty"`
 | 
			
		||||
	Kbfspath__           *KBFSPath             `codec:"kbfspath,omitempty" json:"kbfspath,omitempty"`
 | 
			
		||||
	Emoji__              *Emoji                `codec:"emoji,omitempty" json:"emoji,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *UITextDecoration) Typ() (ret UITextDecorationTyp, err error) {
 | 
			
		||||
@@ -1605,6 +1657,11 @@ func (o *UITextDecoration) Typ() (ret UITextDecorationTyp, err error) {
 | 
			
		||||
			err = errors.New("unexpected nil value for Kbfspath__")
 | 
			
		||||
			return ret, err
 | 
			
		||||
		}
 | 
			
		||||
	case UITextDecorationTyp_EMOJI:
 | 
			
		||||
		if o.Emoji__ == nil {
 | 
			
		||||
			err = errors.New("unexpected nil value for Emoji__")
 | 
			
		||||
			return ret, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return o.Typ__, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1679,6 +1736,16 @@ func (o UITextDecoration) Kbfspath() (res KBFSPath) {
 | 
			
		||||
	return *o.Kbfspath__
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o UITextDecoration) Emoji() (res Emoji) {
 | 
			
		||||
	if o.Typ__ != UITextDecorationTyp_EMOJI {
 | 
			
		||||
		panic("wrong case accessed")
 | 
			
		||||
	}
 | 
			
		||||
	if o.Emoji__ == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return *o.Emoji__
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUITextDecorationWithPayment(v TextPayment) UITextDecoration {
 | 
			
		||||
	return UITextDecoration{
 | 
			
		||||
		Typ__:     UITextDecorationTyp_PAYMENT,
 | 
			
		||||
@@ -1728,6 +1795,13 @@ func NewUITextDecorationWithKbfspath(v KBFSPath) UITextDecoration {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUITextDecorationWithEmoji(v Emoji) UITextDecoration {
 | 
			
		||||
	return UITextDecoration{
 | 
			
		||||
		Typ__:   UITextDecorationTyp_EMOJI,
 | 
			
		||||
		Emoji__: &v,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o UITextDecoration) DeepCopy() UITextDecoration {
 | 
			
		||||
	return UITextDecoration{
 | 
			
		||||
		Typ__: o.Typ__.DeepCopy(),
 | 
			
		||||
@@ -1780,6 +1854,13 @@ func (o UITextDecoration) DeepCopy() UITextDecoration {
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.Kbfspath__),
 | 
			
		||||
		Emoji__: (func(x *Emoji) *Emoji {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			tmp := (*x).DeepCopy()
 | 
			
		||||
			return &tmp
 | 
			
		||||
		})(o.Emoji__),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1917,6 +1998,50 @@ func (o UIChatSearchConvHits) DeepCopy() UIChatSearchConvHits {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIChatSearchTeamHits struct {
 | 
			
		||||
	Hits             []keybase1.TeamSearchItem `codec:"hits" json:"hits"`
 | 
			
		||||
	SuggestedMatches bool                      `codec:"suggestedMatches" json:"suggestedMatches"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o UIChatSearchTeamHits) DeepCopy() UIChatSearchTeamHits {
 | 
			
		||||
	return UIChatSearchTeamHits{
 | 
			
		||||
		Hits: (func(x []keybase1.TeamSearchItem) []keybase1.TeamSearchItem {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make([]keybase1.TeamSearchItem, len(x))
 | 
			
		||||
			for i, v := range x {
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[i] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Hits),
 | 
			
		||||
		SuggestedMatches: o.SuggestedMatches,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIChatSearchBotHits struct {
 | 
			
		||||
	Hits             []keybase1.FeaturedBot `codec:"hits" json:"hits"`
 | 
			
		||||
	SuggestedMatches bool                   `codec:"suggestedMatches" json:"suggestedMatches"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o UIChatSearchBotHits) DeepCopy() UIChatSearchBotHits {
 | 
			
		||||
	return UIChatSearchBotHits{
 | 
			
		||||
		Hits: (func(x []keybase1.FeaturedBot) []keybase1.FeaturedBot {
 | 
			
		||||
			if x == nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			ret := make([]keybase1.FeaturedBot, len(x))
 | 
			
		||||
			for i, v := range x {
 | 
			
		||||
				vCopy := v.DeepCopy()
 | 
			
		||||
				ret[i] = vCopy
 | 
			
		||||
			}
 | 
			
		||||
			return ret
 | 
			
		||||
		})(o.Hits),
 | 
			
		||||
		SuggestedMatches: o.SuggestedMatches,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UIChatPayment struct {
 | 
			
		||||
	Username      string  `codec:"username" json:"username"`
 | 
			
		||||
	FullName      string  `codec:"fullName" json:"fullName"`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.6 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
// Auto-generated to Go types using avdl-compiler v1.4.8 (https://github.com/keybase/node-avdl-compiler)
 | 
			
		||||
//   Input file: ../client/protocol/avdl/chat1/commands.avdl
 | 
			
		||||
 | 
			
		||||
package chat1
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user