Compare commits
	
		
			158 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b979aff270 | ||
|   | b293e3fa75 | ||
|   | 21eb37e471 | ||
|   | d3b60cc445 | ||
|   | 7466e1d014 | ||
|   | 2a7f28606c | ||
|   | 0450482e6e | ||
|   | ee5d9b43b5 | ||
|   | 3a8857c8c9 | ||
|   | be3dfb251d | ||
|   | 4e11e29f70 | ||
|   | 763bb95cea | ||
|   | 668e7407e6 | ||
|   | c147ba1da1 | ||
|   | 10f044c3dd | ||
|   | ce5140febd | ||
|   | 858cdc86f5 | ||
|   | 9a25297d51 | ||
|   | e24f7f5151 | ||
|   | eff5f1e119 | ||
|   | afcd362cd1 | ||
|   | 0452be0cb3 | ||
|   | 1624f10773 | ||
|   | 8764be7461 | ||
|   | 5dd15ef8e7 | ||
|   | 4ac6366706 | ||
|   | adc0912efa | ||
|   | 536823ce55 | ||
|   | 207cd24edb | ||
|   | b039da1eba | ||
|   | 8fcd0f3b6f | ||
|   | 16fde6935c | ||
|   | 9592cff9fa | ||
|   | 109148988c | ||
|   | cf13fff7d2 | ||
|   | a9d8ac8bc0 | ||
|   | 1a4717b366 | ||
|   | 6cadf12260 | ||
|   | 19d47784bd | ||
|   | b89102c5fc | ||
|   | 4f20ebead3 | ||
|   | a9f89dbc64 | ||
|   | 58ea1e07d2 | ||
|   | 6de4c7e971 | ||
|   | 03dc51ffa2 | ||
|   | aef2dcdfdd | ||
|   | 0494119bf4 | ||
|   | 0a17e21119 | ||
|   | 52e2f926f4 | ||
|   | 611fb279bc | ||
|   | 41b4e64be9 | ||
|   | 0d7315249d | ||
|   | 4913766d58 | ||
|   | 92da8c7044 | ||
|   | 9dba3d5385 | ||
|   | 2d3c26a4b2 | ||
|   | 8eba2d3e50 | ||
|   | a8d4a27de1 | ||
|   | c42167c6f4 | ||
|   | 44d182e2f9 | ||
|   | ad95e35687 | ||
|   | 640a9995f4 | ||
|   | 95625f6871 | ||
|   | 2c20f72a9c | ||
|   | 5ad788e768 | ||
|   | ed98c586c6 | ||
|   | 3e865708d6 | ||
|   | c3bcbd63c0 | ||
|   | 29e29439ee | ||
|   | 0c19716f44 | ||
|   | b24e1bafa1 | ||
|   | 64b899ac89 | ||
|   | aa274e5ab7 | ||
|   | 7b3eaf3ccf | ||
|   | 1a5353d768 | ||
|   | eff41759bc | ||
|   | c23252ab53 | ||
|   | 1a3c57a031 | ||
|   | 4cc2c914e6 | ||
|   | cbb46293ab | ||
|   | 765e00c949 | ||
|   | 662359908b | ||
|   | 0d99766686 | ||
|   | ae3bc3358b | ||
|   | 1e0b4532bd | ||
|   | 4f8b19c686 | ||
|   | 84ab223b81 | ||
|   | 2bb21262d4 | ||
|   | 3188a9ffe6 | ||
|   | 61569a8610 | ||
|   | 075a84427f | ||
|   | 950f2759bd | ||
|   | 25c82ddf02 | ||
|   | 2d98df6122 | ||
|   | 219a5453f9 | ||
|   | 214a6a1386 | ||
|   | e7781dc79c | ||
|   | 10c4bd1ac8 | ||
|   | a42e488e58 | ||
|   | 06eb89b05b | ||
|   | 91c58ec027 | ||
|   | 8b26e42a3a | ||
|   | acca011f15 | ||
|   | 2f59abdda7 | ||
|   | 17747a5c88 | ||
|   | cec63546ff | ||
|   | 75f67d2de4 | ||
|   | 712385ffd5 | ||
|   | ad90cf09fe | ||
|   | f9928c9e25 | ||
|   | a0741d99b8 | ||
|   | c63f08c811 | ||
|   | 58b6c4d277 | ||
|   | 27c02549c8 | ||
|   | 88d371c71c | ||
|   | b339524613 | ||
|   | d5feda5c8a | ||
|   | 2f506425c2 | ||
|   | e8167ee3d7 | ||
|   | 2f5e211065 | ||
|   | 39f4fb3446 | ||
|   | 56159b9bce | ||
|   | b2af76e7dc | ||
|   | 491fe35397 | ||
|   | b451285af7 | ||
|   | 63a1847cdc | ||
|   | 4e50fd8649 | ||
|   | dfdffa0027 | ||
|   | ebd2073144 | ||
|   | 1e94b716fb | ||
|   | 2a41abb3d1 | ||
|   | 2d2bebe976 | ||
|   | e1629994bd | ||
|   | e3d8fe4fd8 | ||
|   | 23d8742f0d | ||
|   | 3b6a8be07b | ||
|   | 71a5b72aff | ||
|   | 213bf349c3 | ||
|   | a94fe55886 | ||
|   | 9b22f16497 | ||
|   | 2977a5957e | ||
|   | f70d1c897a | ||
|   | a4a3525265 | ||
|   | a6dd8446e4 | ||
|   | 7bf9e1cfb3 | ||
|   | f291832a77 | ||
|   | 1fee323247 | ||
|   | a41accd033 | ||
|   | 37f7caf7f3 | ||
|   | 5847f7758c | ||
|   | bce736993e | ||
|   | 5636992446 | ||
|   | f996a2b7ae | ||
|   | 587de96ab3 | ||
|   | 80eb1cd202 | ||
|   | bbf594c815 | ||
|   | 2f0f2ee40d | ||
|   | 96022d3aaf | 
							
								
								
									
										2
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| Dockerfile | ||||
| tgs.Dockerfile | ||||
							
								
								
									
										71
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| # For most projects, this workflow file will not need changing; you simply need | ||||
| # to commit it to your repository. | ||||
| # | ||||
| # You may wish to alter this file to override the set of languages analyzed, | ||||
| # or to provide custom queries or build logic. | ||||
| name: "CodeQL" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: [master] | ||||
|   pull_request: | ||||
|     # The branches below must be a subset of the branches above | ||||
|     branches: [master] | ||||
|   schedule: | ||||
|     - cron: '0 16 * * 1' | ||||
|  | ||||
| jobs: | ||||
|   analyze: | ||||
|     name: Analyze | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         # Override automatic language detection by changing the below list | ||||
|         # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] | ||||
|         language: ['go'] | ||||
|         # Learn more... | ||||
|         # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection | ||||
|  | ||||
|     steps: | ||||
|     - name: Checkout repository | ||||
|       uses: actions/checkout@v2 | ||||
|       with: | ||||
|         # We must fetch at least the immediate parents so that if this is | ||||
|         # a pull request then we can checkout the head. | ||||
|         fetch-depth: 2 | ||||
|  | ||||
|     # If this run was triggered by a pull request event, then checkout | ||||
|     # the head of the pull request instead of the merge commit. | ||||
|     - run: git checkout HEAD^2 | ||||
|       if: ${{ github.event_name == 'pull_request' }} | ||||
|  | ||||
|     # Initializes the CodeQL tools for scanning. | ||||
|     - name: Initialize CodeQL | ||||
|       uses: github/codeql-action/init@v1 | ||||
|       with: | ||||
|         languages: ${{ matrix.language }} | ||||
|         # If you wish to specify custom queries, you can do so here or in a config file. | ||||
|         # By default, queries listed here will override any specified in a config file.  | ||||
|         # Prefix the list here with "+" to use these queries and those in the config file. | ||||
|         # queries: ./path/to/local/query, your-org/your-repo/queries@main | ||||
|  | ||||
|     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java). | ||||
|     # If this step fails, then you should remove it and run the build manually (see below) | ||||
|     - name: Autobuild | ||||
|       uses: github/codeql-action/autobuild@v1 | ||||
|  | ||||
|     # ℹ️ Command-line programs to run using the OS shell. | ||||
|     # 📚 https://git.io/JvXDl | ||||
|  | ||||
|     # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines | ||||
|     #    and modify them (or add more) to build your code if your project | ||||
|     #    uses a compiled language | ||||
|  | ||||
|     #- run: | | ||||
|     #   make bootstrap | ||||
|     #   make release | ||||
|  | ||||
|     - name: Perform CodeQL Analysis | ||||
|       uses: github/codeql-action/analyze@v1 | ||||
							
								
								
									
										58
									
								
								.github/workflows/development.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								.github/workflows/development.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| name: Development | ||||
| on: [push, pull_request] | ||||
| jobs: | ||||
|   lint: | ||||
|     name: golangci-lint | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|         with: | ||||
|           fetch-depth: 20 | ||||
|       - name: Run golangci-lint | ||||
|         uses: golangci/golangci-lint-action@v2 | ||||
|         with: | ||||
|           version: v1.29 | ||||
|           args: "-v --new-from-rev HEAD~5" | ||||
|   test-build-upload: | ||||
|     strategy: | ||||
|       matrix: | ||||
|         go-version: [1.15.x, 1.16.x] | ||||
|         platform: [ubuntu-latest] | ||||
|     runs-on: ${{ matrix.platform }} | ||||
|     steps: | ||||
|     - name: Install Go | ||||
|       uses: actions/setup-go@v2 | ||||
|       with: | ||||
|         go-version: ${{ matrix.go-version }} | ||||
|         stable: false | ||||
|     - 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 -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 -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 -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.16') | ||||
|       uses: actions/upload-artifact@v2 | ||||
|       with: | ||||
|         name: matterbridge-linux-64bit | ||||
|         path: output/lin | ||||
|     - name: Upload windows 64-bit | ||||
|       if: startsWith(matrix.go-version,'1.16') | ||||
|       uses: actions/upload-artifact@v2 | ||||
|       with: | ||||
|         name: matterbridge-windows-64bit | ||||
|         path: output/win | ||||
|     - name: Upload darwin 64-bit | ||||
|       if: startsWith(matrix.go-version,'1.16') | ||||
|       uses: actions/upload-artifact@v2 | ||||
|       with: | ||||
|         name: matterbridge-darwin-64bit | ||||
|         path: output/mac | ||||
| @@ -91,7 +91,6 @@ linters-settings: | ||||
|     # Correct spellings using locale preferences for US or UK. | ||||
|     # Default is to use a neutral variety of English. | ||||
|     # Setting locale to US will correct the British spelling of 'colour' to 'color'. | ||||
|     locale: US | ||||
|   lll: | ||||
|     # max line length, lines longer will be reported. Default is 120. | ||||
|     # '\t' is counted as 1 character by default, and can be changed with the tab-width option | ||||
| @@ -176,7 +175,13 @@ linters: | ||||
|     - prealloc | ||||
|     - wsl | ||||
|     - gomnd | ||||
|  | ||||
|     - godox | ||||
|     - goerr113 | ||||
|     - testpackage | ||||
|     - godot | ||||
|     - interfacer | ||||
|     - goheader | ||||
|     - noctx | ||||
|  | ||||
| # rules to deal with reported isues | ||||
| issues: | ||||
|   | ||||
							
								
								
									
										56
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,56 +0,0 @@ | ||||
| language: go | ||||
| go_import_path: github.com/42wim/matterbridge | ||||
|  | ||||
| # We have everything vendored so this helps TravisCI not run `go get ...`. | ||||
| install: true | ||||
|  | ||||
| git: | ||||
|   depth: 200 | ||||
|  | ||||
| notifications: | ||||
|   email: false | ||||
|  | ||||
| branches: | ||||
|   only: | ||||
|   - master | ||||
|   - /.*/ | ||||
|  | ||||
| jobs: | ||||
|   include: | ||||
|   - stage: lint | ||||
|     # Run linting in one Go environment only. | ||||
|     script: ./ci/lint.sh | ||||
|     go: 1.14.x | ||||
|     env: | ||||
|     - GO111MODULE=on | ||||
|     - GOLANGCI_VERSION="v1.23.7" | ||||
|   - stage: test | ||||
|     # Run tests in a combination of Go environments. | ||||
|     script: ./ci/test.sh | ||||
|     go: 1.13.x | ||||
|     env: | ||||
|     - GOFLAGS=-mod=vendor | ||||
|   - script: ./ci/test.sh | ||||
|     go: 1.13.x | ||||
|     env: | ||||
|     - GO111MODULE=on | ||||
|   - script: ./ci/test.sh | ||||
|     go: 1.14.x | ||||
|     env: | ||||
|     - GO111MODULE=on | ||||
|     - REPORT_COVERAGE=1 | ||||
|     - BINDEPLOY=1 | ||||
|  | ||||
| before_deploy: /bin/bash ci/bintray.sh | ||||
|  | ||||
| deploy: | ||||
|   on: | ||||
|     all_branches: true | ||||
|     condition: $BINDEPLOY = 1 | ||||
|   provider: bintray | ||||
|   edge: | ||||
|     branch: v1.8.47 | ||||
|   file: ci/deploy.json | ||||
|   user: 42wim | ||||
|   key: | ||||
|     secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" | ||||
							
								
								
									
										20
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,16 +1,14 @@ | ||||
| FROM alpine:edge AS builder | ||||
| FROM alpine AS builder | ||||
|  | ||||
| COPY . /go/src/github.com/42wim/matterbridge | ||||
| RUN apk update && apk add go git gcc musl-dev \ | ||||
|         && cd /go/src/github.com/42wim/matterbridge \ | ||||
|         && export GOPATH=/go \ | ||||
|         && go get \ | ||||
|         && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge | ||||
| COPY . /go/src/matterbridge | ||||
| RUN apk --no-cache add go git \ | ||||
|         && cd /go/src/matterbridge \ | ||||
|         && go build -mod vendor -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge | ||||
|  | ||||
| FROM alpine:edge | ||||
| FROM alpine | ||||
| RUN apk --no-cache add ca-certificates mailcap | ||||
| COPY --from=builder /bin/matterbridge /bin/matterbridge | ||||
| RUN mkdir /etc/matterbridge | ||||
| RUN touch /etc/matterbridge/matterbridge.toml | ||||
| RUN ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml | ||||
| 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"] | ||||
|   | ||||
							
								
								
									
										106
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								README.md
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ And more... | ||||
| --- | ||||
|  | ||||
| [](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 /> | ||||
|  | ||||
| @@ -71,7 +70,7 @@ And more... | ||||
|   - [Changelog](#changelog) | ||||
|   - [FAQ](#faq) | ||||
|   - [Related projects](#related-projects) | ||||
|   - [Articles](#articles) | ||||
|   - [Articles / Tutorials](#articles--tutorials) | ||||
|   - [Thanks](#thanks) | ||||
|  | ||||
| ## Features | ||||
| @@ -92,14 +91,18 @@ And more... | ||||
| - [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 | ||||
| - [Mattermost](https://github.com/mattermost/mattermost-server/) | ||||
| - [Microsoft Teams](https://teams.microsoft.com) | ||||
| - [Mumble](https://www.mumble.info/) | ||||
| - [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/) | ||||
| - ~~[Steam](https://store.steampowered.com/)~~ | ||||
|   - Not supported anymore, see [here](https://github.com/Philipp15b/go-steam/issues/94) for more info. | ||||
| - [Telegram](https://telegram.org) | ||||
| - [Twitch](https://twitch.tv) | ||||
| - [VK](https://vk.com/) | ||||
| - [WhatsApp](https://www.whatsapp.com/) | ||||
| - [XMPP](https://xmpp.org) | ||||
| - [Zulip](https://zulipchat.com) | ||||
| @@ -107,10 +110,15 @@ And more... | ||||
| ### 3rd party via matterbridge api | ||||
|  | ||||
| - [Discourse](https://github.com/DeclanHoare/matterbabble) | ||||
| - [Facebook messenger](https://github.com/powerjungle/fbridge-asyncio) | ||||
| - [Facebook messenger](https://github.com/VictorNine/fbridge) | ||||
| - [Minecraft](https://github.com/elytra/MatterLink) | ||||
| - [Minecraft](https://github.com/raws/mattercraft) | ||||
| - [Minecraft](https://gitlab.com/Programie/MatterBukkit) | ||||
| - [Reddit](https://github.com/bonehurtingjuice/mattereddit) | ||||
| - [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430) | ||||
| - [MatterAMXX](https://github.com/GabeIggy/MatterAMXX) | ||||
| - [Vintage Story](https://github.com/NikkyAI/vs-matterbridge) | ||||
|  | ||||
| ### API | ||||
|  | ||||
| @@ -119,12 +127,16 @@ More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/ | ||||
|  | ||||
| Used by the projects below. Feel free to make a PR to add your project to this list. | ||||
|  | ||||
| - [MatterLink](https://github.com/elytra/MatterLink) (Matterbridge link for Minecraft Server chat) | ||||
| - [MatterLink](https://github.com/elytra/MatterLink) (Matterbridge link for Minecraft Forge server chat, archived) | ||||
| - [MatterCraft](https://github.com/raws/mattercraft) (Matterbridge link for Minecraft Forge server chat) | ||||
| - [MatterBukkit](https://gitlab.com/Programie/MatterBukkit) (Matterbridge link for Minecraft Bukkit/Spigot server chat) | ||||
| - [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot) | ||||
| - [Mattereddit](https://github.com/bonehurtingjuice/mattereddit) (Reddit chat support) | ||||
| - [fbridge-asyncio](https://github.com/powerjungle/fbridge-asyncio) (Facebook messenger support) | ||||
| - [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support) | ||||
| - [matterbabble](https://github.com/DeclanHoare/matterbabble) (Discourse support) | ||||
| - [MatterAMXX](https://forums.alliedmods.net/showthread.php?t=319430) (Counter-Strike, half-life and more via AMXX mod) | ||||
| - [Vintage Story](https://github.com/NikkyAI/vs-matterbridge) | ||||
|  | ||||
| ## Chat with us | ||||
|  | ||||
| @@ -145,36 +157,37 @@ Questions or want to test on your favorite platform? Join below: | ||||
|  | ||||
| ## Screenshots | ||||
|  | ||||
| See https://github.com/42wim/matterbridge/wiki | ||||
| See <https://github.com/42wim/matterbridge/wiki> | ||||
|  | ||||
| ## Installing / upgrading | ||||
|  | ||||
| ### Binaries | ||||
|  | ||||
| - Latest stable release [v1.17.5](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.22.1](https://github.com/42wim/matterbridge/releases/latest) | ||||
| - Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts. | ||||
|  | ||||
| To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. | ||||
| To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest). On \*nix platforms you may need to make the binary executable - you can do this by running `chmod a+x` on the binary (example: `chmod a+x matterbridge-1.20.0-linux-64bit`). After downloading (and making the binary executable, if necessary), 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) | ||||
| - [scoop](https://github.com/42wim/scoop-bucket) | ||||
|  | ||||
| ## Building | ||||
|  | ||||
| Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest) | ||||
|  | ||||
| If you really want to build from source, follow these instructions: | ||||
| Go 1.12+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed. | ||||
| Go 1.13+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed. | ||||
|  | ||||
|  | ||||
| ``` | ||||
| ```bash | ||||
| go get github.com/42wim/matterbridge | ||||
| ``` | ||||
|  | ||||
| You should now have matterbridge binary in the ~/go/bin directory: | ||||
|  | ||||
| ``` | ||||
| ```bash | ||||
| $ ls ~/go/bin/ | ||||
| matterbridge | ||||
| ``` | ||||
| @@ -257,7 +270,7 @@ RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> " | ||||
|  | ||||
| See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration. | ||||
|  | ||||
| ``` | ||||
| ```bash | ||||
| Usage of ./matterbridge: | ||||
|   -conf string | ||||
|         config file (default "matterbridge.toml") | ||||
| @@ -294,18 +307,23 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | ||||
| - [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support) | ||||
| - [isla](https://github.com/alphachung/isla) (Bot for Discord-Telegram groups used alongside matterbridge) | ||||
| - [matterbabble](https://github.com/DeclanHoare/matterbabble) (Connect Discourse threads to Matterbridge) | ||||
| - [nextcloud talk](https://github.com/nextcloud/talk_matterbridge) (Integrates matterbridge in Nextcloud Talk) | ||||
| - [mattercraft](https://github.com/raws/mattercraft) (Minecraft bridge) | ||||
| - [vs-matterbridge](https://github.com/NikkyAI/vs-matterbridge) (Vintage Story bridge) | ||||
|  | ||||
| ## Articles | ||||
| ## Articles / Tutorials | ||||
|  | ||||
| - [matterbridge on kubernetes](https://medium.freecodecamp.org/using-kubernetes-to-deploy-a-chat-gateway-or-when-technology-works-like-its-supposed-to-a169a8cd69a3) | ||||
| - https://mattermost.com/blog/connect-irc-to-mattermost/ | ||||
| - https://blog.valvin.fr/2016/09/17/mattermost-et-un-channel-irc-cest-possible/ | ||||
| - https://blog.brightscout.com/top-10-mattermost-integrations/ | ||||
| - http://bencey.co.nz/2018/09/17/bridge/ | ||||
| - https://www.algoo.fr/blog/2018/01/19/recouvrez-votre-liberte-en-quittant-slack-pour-un-mattermost-auto-heberge/ | ||||
| - 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://mattermost.com/blog/connect-irc-to-mattermost/> | ||||
| - <https://blog.valvin.fr/2016/09/17/mattermost-et-un-channel-irc-cest-possible/> | ||||
| - <https://blog.brightscout.com/top-10-mattermost-integrations/> | ||||
| - <https://www.algoo.fr/blog/2018/01/19/recouvrez-votre-liberte-en-quittant-slack-pour-un-mattermost-auto-heberge/> | ||||
| - <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> | ||||
| - <https://nextcloud.com/blog/bridging-chat-services-in-talk/> | ||||
| - Youtube: [whatsapp - telegram bridging](https://www.youtube.com/watch?v=W-VXISoKtNc) | ||||
|  | ||||
| ## Thanks | ||||
|  | ||||
| @@ -318,24 +336,28 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ) | ||||
|  | ||||
| Matterbridge wouldn't exist without these libraries: | ||||
|  | ||||
| - discord - https://github.com/bwmarrin/discordgo | ||||
| - echo - https://github.com/labstack/echo | ||||
| - gitter - https://github.com/sromku/go-gitter | ||||
| - gops - https://github.com/google/gops | ||||
| - gozulipbot - https://github.com/ifo/gozulipbot | ||||
| - irc - https://github.com/lrstanley/girc | ||||
| - keybase - https://github.com/keybase/go-keybase-chat-bot | ||||
| - matrix - https://github.com/matrix-org/gomatrix | ||||
| - 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 | ||||
| - tengo - https://github.com/d5/tengo | ||||
| - whatsapp - https://github.com/Rhymen/go-whatsapp/ | ||||
| - xmpp - https://github.com/mattn/go-xmpp | ||||
| - zulip - https://github.com/ifo/gozulipbot | ||||
| - discord - <https://github.com/bwmarrin/discordgo> | ||||
| - echo - <https://github.com/labstack/echo> | ||||
| - gitter - <https://github.com/sromku/go-gitter> | ||||
| - gops - <https://github.com/google/gops> | ||||
| - gozulipbot - <https://github.com/ifo/gozulipbot> | ||||
| - gumble - <https://github.com/layeh/gumble> | ||||
| - irc - <https://github.com/lrstanley/girc> | ||||
| - keybase - <https://github.com/keybase/go-keybase-chat-bot> | ||||
| - matrix - <https://github.com/matrix-org/gomatrix> | ||||
| - mattermost - <https://github.com/mattermost/mattermost-server> | ||||
| - msgraph.go - <https://github.com/yaegashi/msgraph.go> | ||||
| - mumble - <https://github.com/layeh/gumble> | ||||
| - nctalk - <https://github.com/gary-kim/go-nc-talk> | ||||
| - 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> | ||||
| - tengo - <https://github.com/d5/tengo> | ||||
| - vk - <https://github.com/SevereCloud/vksdk> | ||||
| - whatsapp - <https://github.com/Rhymen/go-whatsapp> | ||||
| - xmpp - <https://github.com/mattn/go-xmpp> | ||||
| - zulip - <https://github.com/ifo/gozulipbot> | ||||
|  | ||||
| <!-- Links --> | ||||
|  | ||||
| @@ -347,7 +369,7 @@ Matterbridge wouldn't exist without these libraries: | ||||
| [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-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA | ||||
| [mb-slack]: https://join.slack.com/t/matterbridgechat/shared_invite/zt-2ourq2h2-7YvyYBq2WFGC~~zEzA68_Q | ||||
| [mb-telegram]: https://t.me/Matterbridge | ||||
| [mb-twitch]: https://www.twitch.tv/matterbridge | ||||
| [mb-whatsapp]: https://www.whatsapp.com/ | ||||
|   | ||||
| @@ -6,17 +6,20 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"gopkg.in/olahol/melody.v1" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"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 { | ||||
| 	Messages ring.Ring | ||||
| 	sync.RWMutex | ||||
| 	*bridge.Config | ||||
| 	mrouter *melody.Melody | ||||
| } | ||||
|  | ||||
| type Message struct { | ||||
| @@ -32,6 +35,32 @@ func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	e := echo.New() | ||||
| 	e.HideBanner = true | ||||
| 	e.HidePort = true | ||||
|  | ||||
| 	b.mrouter = melody.New() | ||||
| 	b.mrouter.HandleMessage(func(s *melody.Session, msg []byte) { | ||||
| 		message := config.Message{} | ||||
| 		err := json.Unmarshal(msg, &message) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("failed to decode message from byte[] '%s'", string(msg)) | ||||
| 			return | ||||
| 		} | ||||
| 		b.handleWebsocketMessage(message, s) | ||||
| 	}) | ||||
| 	b.mrouter.HandleConnect(func(session *melody.Session) { | ||||
| 		greet := b.getGreeting() | ||||
| 		data, err := json.Marshal(greet) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("failed to encode message '%v'", greet) | ||||
| 			return | ||||
| 		} | ||||
| 		err = session.Write(data) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("failed to write message '%s'", string(data)) | ||||
| 			return | ||||
| 		} | ||||
| 		// TODO: send message history buffer from `b.Messages` here | ||||
| 	}) | ||||
|  | ||||
| 	b.Messages = ring.Ring{} | ||||
| 	if b.GetInt("Buffer") != 0 { | ||||
| 		b.Messages.SetCapacity(b.GetInt("Buffer")) | ||||
| @@ -41,9 +70,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") == "" { | ||||
| @@ -58,13 +95,13 @@ func New(cfg *bridge.Config) bridge.Bridger { | ||||
| func (b *API) Connect() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *API) Disconnect() error { | ||||
| 	return nil | ||||
|  | ||||
| } | ||||
|  | ||||
| func (b *API) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	return nil | ||||
|  | ||||
| } | ||||
|  | ||||
| func (b *API) Send(msg config.Message) (string, error) { | ||||
| @@ -74,7 +111,14 @@ func (b *API) Send(msg config.Message) (string, error) { | ||||
| 	if msg.Event == config.EventMsgDelete { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	b.Messages.Enqueue(&msg) | ||||
| 	b.Log.Debugf("enqueueing message from %s on ring buffer", msg.Username) | ||||
| 	b.Messages.Enqueue(msg) | ||||
|  | ||||
| 	data, err := json.Marshal(msg) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("failed to encode message  '%s'", msg) | ||||
| 	} | ||||
| 	_ = b.mrouter.Broadcast(data) | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| @@ -106,18 +150,23 @@ 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 | ||||
| 	} | ||||
| 	c.Response().Flush() | ||||
| 	for { | ||||
| 		// TODO: this causes issues, messages should be broadcasted to all connected clients | ||||
| 		msg := b.Messages.Dequeue() | ||||
| 		if msg != nil { | ||||
| 			if err := json.NewEncoder(c.Response()).Encode(msg); err != nil { | ||||
| @@ -128,3 +177,31 @@ func (b *API) handleStream(c echo.Context) error { | ||||
| 		time.Sleep(200 * time.Millisecond) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *API) handleWebsocketMessage(message config.Message, s *melody.Session) { | ||||
| 	message.Channel = "api" | ||||
| 	message.Protocol = "api" | ||||
| 	message.Account = b.Account | ||||
| 	message.ID = "" | ||||
| 	message.Timestamp = time.Now() | ||||
|  | ||||
| 	data, err := json.Marshal(message) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("failed to encode message for loopback '%v'", message) | ||||
| 		return | ||||
| 	} | ||||
| 	_ = b.mrouter.BroadcastOthers(data, s) | ||||
|  | ||||
| 	b.Log.Debugf("Sending websocket message from %s on %s to gateway", message.Username, "api") | ||||
| 	b.Remote <- message | ||||
| } | ||||
|  | ||||
| func (b *API) handleWebsocket(c echo.Context) error { | ||||
| 	err := b.mrouter.HandleRequest(c.Response(), c.Request()) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("error in websocket handling  '%v'", err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -86,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) | ||||
| 	} | ||||
| @@ -95,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) | ||||
| 	} | ||||
| @@ -103,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) | ||||
| 	} | ||||
| @@ -111,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) | ||||
| 	} | ||||
| @@ -119,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" | ||||
| @@ -25,8 +26,11 @@ const ( | ||||
| 	EventAPIConnected      = "api_connected" | ||||
| 	EventUserTyping        = "user_typing" | ||||
| 	EventGetChannelMembers = "get_channel_members" | ||||
| 	EventNoticeIRC         = "notice_irc" | ||||
| ) | ||||
|  | ||||
| const ParentIDNotFound = "msg-parent-not-found" | ||||
|  | ||||
| type Message struct { | ||||
| 	Text      string    `json:"text"` | ||||
| 	Channel   string    `json:"channel"` | ||||
| @@ -43,6 +47,14 @@ type Message struct { | ||||
| 	Extra     map[string][]interface{} | ||||
| } | ||||
|  | ||||
| func (m Message) ParentNotFound() bool { | ||||
| 	return m.ParentID == ParentIDNotFound | ||||
| } | ||||
|  | ||||
| func (m Message) ParentValid() bool { | ||||
| 	return m.ParentID != "" && !m.ParentNotFound() | ||||
| } | ||||
|  | ||||
| type FileInfo struct { | ||||
| 	Name    string | ||||
| 	Data    *[]byte | ||||
| @@ -93,11 +105,13 @@ type Protocol struct { | ||||
| 	JoinDelay              string // all protocols | ||||
| 	Label                  string // all protocols | ||||
| 	Login                  string // mattermost, matrix | ||||
| 	LogFile                string // general | ||||
| 	MediaDownloadBlackList []string | ||||
| 	MediaDownloadPath      string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server. | ||||
| 	MediaDownloadSize      int    // all protocols | ||||
| 	MediaServerDownload    string | ||||
| 	MediaServerUpload      string | ||||
| 	MediaConvertTgs        string     // telegram | ||||
| 	MediaConvertWebPToPNG  bool       // telegram | ||||
| 	MessageDelay           int        // IRC, time in millisecond to wait between messages | ||||
| 	MessageFormat          string     // telegram | ||||
| @@ -114,7 +128,7 @@ type Protocol struct { | ||||
| 	NicksPerRow            int        // mattermost, slack | ||||
| 	NoHomeServerSuffix     bool       // matrix | ||||
| 	NoSendJoinPart         bool       // all protocols | ||||
| 	NoTLS                  bool       // mattermost | ||||
| 	NoTLS                  bool       // mattermost, xmpp | ||||
| 	Password               string     // IRC,mattermost,XMPP,matrix | ||||
| 	PrefixMessagesWithNick bool       // mattemost, slack | ||||
| 	PreserveThreading      bool       // slack | ||||
| @@ -151,7 +165,7 @@ type Protocol struct { | ||||
| 	UseTLS                 bool       // IRC | ||||
| 	UseDiscriminator       bool       // discord | ||||
| 	UseFirstName           bool       // telegram | ||||
| 	UseUserName            bool       // discord | ||||
| 	UseUserName            bool       // discord, matrix | ||||
| 	UseInsecureURL         bool       // telegram | ||||
| 	VerboseJoinPart        bool       // IRC | ||||
| 	WebhookBindAddress     string     // mattermost, slack | ||||
| @@ -210,6 +224,7 @@ type BridgeValues struct { | ||||
| 	WhatsApp           map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results | ||||
| 	Zulip              map[string]Protocol | ||||
| 	Keybase            map[string]Protocol | ||||
| 	Mumble             map[string]Protocol | ||||
| 	General            Protocol | ||||
| 	Tengo              Tengo | ||||
| 	Gateway            []Gateway | ||||
| @@ -219,6 +234,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) | ||||
| @@ -246,6 +262,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 | ||||
| 	} | ||||
| @@ -303,6 +328,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() | ||||
| @@ -362,6 +393,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 { | ||||
|   | ||||
| @@ -2,13 +2,13 @@ package bdiscord | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/discord/transmitter" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| ) | ||||
| @@ -20,12 +20,9 @@ type Bdiscord struct { | ||||
|  | ||||
| 	c *discordgo.Session | ||||
|  | ||||
| 	nick            string | ||||
| 	userID          string | ||||
| 	guildID         string | ||||
| 	webhookID       string | ||||
| 	webhookToken    string | ||||
| 	canEditWebhooks bool | ||||
| 	nick    string | ||||
| 	userID  string | ||||
| 	guildID string | ||||
|  | ||||
| 	channelsMutex  sync.RWMutex | ||||
| 	channels       []*discordgo.Channel | ||||
| @@ -34,8 +31,10 @@ type Bdiscord struct { | ||||
| 	membersMutex  sync.RWMutex | ||||
| 	userMemberMap map[string]*discordgo.Member | ||||
| 	nickMemberMap map[string]*discordgo.Member | ||||
| 	webhookCache  map[string]string | ||||
| 	webhookMutex  sync.RWMutex | ||||
|  | ||||
| 	// Webhook specific logic | ||||
| 	useAutoWebhooks bool | ||||
| 	transmitter     *transmitter.Transmitter | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| @@ -43,24 +42,18 @@ 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")) | ||||
|  | ||||
| 	b.useAutoWebhooks = b.GetBool("AutoWebhooks") | ||||
| 	if b.useAutoWebhooks { | ||||
| 		b.Log.Debug("Using automatic webhooks") | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) Connect() error { | ||||
| 	var err error | ||||
| 	var guildFound bool | ||||
| 	token := b.GetString("Token") | ||||
| 	b.Log.Info("Connecting") | ||||
| 	if b.GetString("WebhookURL") == "" { | ||||
| 		b.Log.Info("Connecting using token") | ||||
| 	} else { | ||||
| 		b.Log.Info("Connecting using webhookurl (for posting) and token") | ||||
| 	} | ||||
| 	if !strings.HasPrefix(b.GetString("Token"), "Bot ") { | ||||
| 		token = "Bot " + b.GetString("Token") | ||||
| 	} | ||||
| @@ -82,6 +75,11 @@ func (b *Bdiscord) Connect() error { | ||||
| 	b.c.AddHandler(b.messageDeleteBulk) | ||||
| 	b.c.AddHandler(b.memberAdd) | ||||
| 	b.c.AddHandler(b.memberRemove) | ||||
| 	// Add privileged intent for guild member tracking. This is needed to track nicks | ||||
| 	// for display names and @mention translation | ||||
| 	b.c.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged | | ||||
| 		discordgo.IntentsGuildMembers) | ||||
|  | ||||
| 	err = b.c.Open() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -97,62 +95,107 @@ func (b *Bdiscord) Connect() error { | ||||
| 	serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1) | ||||
| 	b.nick = userinfo.Username | ||||
| 	b.userID = userinfo.ID | ||||
|  | ||||
| 	// Try and find this account's guild, and populate channels | ||||
| 	b.channelsMutex.Lock() | ||||
| 	for _, guild := range guilds { | ||||
| 		if guild.Name == serverName || guild.ID == serverName { | ||||
| 			b.channels, err = b.c.GuildChannels(guild.ID) | ||||
| 			if err != nil { | ||||
| 				break | ||||
| 			} | ||||
| 			b.guildID = guild.ID | ||||
| 			guildFound = true | ||||
| 		// Skip, if the server name does not match the visible name or the ID | ||||
| 		if guild.Name != serverName && guild.ID != serverName { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Complain about an ambiguous Server setting. Two Discord servers could have the same title! | ||||
| 		// For IDs, practically this will never happen. It would only trigger if some server's name is also an ID. | ||||
| 		if b.guildID != "" { | ||||
| 			return fmt.Errorf("found multiple Discord servers with the same name %#v, expected to see only one", serverName) | ||||
| 		} | ||||
|  | ||||
| 		// Getting this guild's channel could result in a permission error | ||||
| 		b.channels, err = b.c.GuildChannels(guild.ID) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("could not get %#v's channels: %w", b.GetString("Server"), err) | ||||
| 		} | ||||
|  | ||||
| 		b.guildID = guild.ID | ||||
| 	} | ||||
| 	b.channelsMutex.Unlock() | ||||
| 	if !guildFound { | ||||
| 		msg := fmt.Sprintf("Server \"%s\" not found", b.GetString("Server")) | ||||
| 		err = errors.New(msg) | ||||
| 		b.Log.Error(msg) | ||||
| 		b.Log.Info("Possible values:") | ||||
|  | ||||
| 	// If we couldn't find a guild, we print extra debug information and return a nice error | ||||
| 	if b.guildID == "" { | ||||
| 		err = fmt.Errorf("could not find Discord server %#v", b.GetString("Server")) | ||||
| 		b.Log.Error(err.Error()) | ||||
|  | ||||
| 		// Print all of the possible server values | ||||
| 		b.Log.Info("Possible server values:") | ||||
| 		for _, guild := range guilds { | ||||
| 			b.Log.Infof("Server=\"%s\" # Server name", guild.Name) | ||||
| 			b.Log.Infof("Server=\"%s\" # Server ID", guild.ID) | ||||
| 			b.Log.Infof("\t- Server=%#v # by name", guild.Name) | ||||
| 			b.Log.Infof("\t- Server=%#v # by ID", guild.ID) | ||||
| 		} | ||||
| 	} | ||||
| 	if err != nil { | ||||
|  | ||||
| 		// If there are no results, we should say that | ||||
| 		if len(guilds) == 0 { | ||||
| 			b.Log.Info("\t- (none found)") | ||||
| 		} | ||||
|  | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	if b.GetString("WebhookURL") == "" { | ||||
| 		for _, channel := range b.channels { | ||||
| 			b.Log.Debugf("found channel %#v", channel) | ||||
| 		} | ||||
| 	} else { | ||||
| 		manageWebhooks := discordgo.PermissionManageWebhooks | ||||
| 		var channelsDenied []string | ||||
| 		for _, info := range b.Channels { | ||||
| 			id := b.getChannelID(info.Name) // note(qaisjp): this readlocks channelsMutex | ||||
| 			b.Log.Debugf("Verifying PermissionManageWebhooks for %s with ID %s", info.ID, id) | ||||
| 	// Legacy note: WebhookURL used to have an actual webhook URL that we would edit, | ||||
| 	// but we stopped doing that due to Discord making rate limits more aggressive. | ||||
| 	// | ||||
| 	// Even older: the same WebhookURL used to be used by every channel, which is usually unexpected. | ||||
| 	// This is no longer possible. | ||||
| 	if b.GetString("WebhookURL") != "" { | ||||
| 		message := "The global WebhookURL setting has been removed. " | ||||
| 		message += "You can get similar \"webhook editing\" behaviour by replacing this line with `AutoWebhooks=true`. " | ||||
| 		message += "If you rely on the old-OLD (non-editing) behaviour, can move the WebhookURL to specific channel sections." | ||||
| 		b.Log.Errorln(message) | ||||
| 		return fmt.Errorf("use of removed WebhookURL setting") | ||||
| 	} | ||||
|  | ||||
| 			perms, permsErr := b.c.UserChannelPermissions(userinfo.ID, id) | ||||
| 			if permsErr != nil { | ||||
| 				b.Log.Warnf("Failed to check PermissionManageWebhooks in channel \"%s\": %s", info.Name, permsErr.Error()) | ||||
| 			} else if perms&manageWebhooks == manageWebhooks { | ||||
| 				continue | ||||
| 	if b.GetInt("debuglevel") > 0 { | ||||
| 		b.Log.Debug("enabling even more discord debug") | ||||
| 		b.c.Debug = true | ||||
| 	} | ||||
|  | ||||
| 	// Initialise webhook management | ||||
| 	b.transmitter = transmitter.New(b.c, b.guildID, "matterbridge", b.useAutoWebhooks) | ||||
| 	b.transmitter.Log = b.Log | ||||
|  | ||||
| 	var webhookChannelIDs []string | ||||
| 	for _, channel := range b.Channels { | ||||
| 		channelID := b.getChannelID(channel.Name) // note(qaisjp): this readlocks channelsMutex | ||||
|  | ||||
| 		// If a WebhookURL was not explicitly provided for this channel, | ||||
| 		// there are two options: just a regular bot message (ugly) or this is should be webhook sent | ||||
| 		if channel.Options.WebhookURL == "" { | ||||
| 			// If it should be webhook sent, we should enforce this via the transmitter | ||||
| 			if b.useAutoWebhooks { | ||||
| 				webhookChannelIDs = append(webhookChannelIDs, channelID) | ||||
| 			} | ||||
| 			channelsDenied = append(channelsDenied, fmt.Sprintf("%#v", info.Name)) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		b.canEditWebhooks = len(channelsDenied) == 0 | ||||
| 		if b.canEditWebhooks { | ||||
| 			b.Log.Info("Can manage webhooks; will edit channel for global webhook on send") | ||||
| 		} else { | ||||
| 			b.Log.Warn("Can't manage webhooks; won't edit channel for global webhook on send") | ||||
| 			b.Log.Warn("Can't manage webhooks in channels: ", strings.Join(channelsDenied, ", ")) | ||||
| 		whID, whToken, ok := b.splitURL(channel.Options.WebhookURL) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("failed to parse WebhookURL %#v for channel %#v", channel.Options.WebhookURL, channel.ID) | ||||
| 		} | ||||
|  | ||||
| 		b.transmitter.AddWebhook(channelID, &discordgo.Webhook{ | ||||
| 			ID:        whID, | ||||
| 			Token:     whToken, | ||||
| 			GuildID:   b.guildID, | ||||
| 			ChannelID: channelID, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	if b.useAutoWebhooks { | ||||
| 		err = b.transmitter.RefreshGuildWebhooks(webhookChannelIDs) | ||||
| 		if err != nil { | ||||
| 			b.Log.WithError(err).Println("transmitter could not refresh guild webhooks") | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	// Obtaining guild members and initializing nickname mapping. | ||||
| 	b.membersMutex.Lock() | ||||
| @@ -191,8 +234,6 @@ 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) | ||||
| @@ -211,79 +252,23 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 		msg.Text = "_" + msg.Text + "_" | ||||
| 	} | ||||
|  | ||||
| 	// use initial webhook configured for the entire Discord account | ||||
| 	isGlobalWebhook := true | ||||
| 	wID := b.webhookID | ||||
| 	wToken := b.webhookToken | ||||
|  | ||||
| 	// check if have a channel specific webhook | ||||
| 	b.channelsMutex.RLock() | ||||
| 	if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok { | ||||
| 		if ci.Options.WebhookURL != "" { | ||||
| 			wID, wToken = b.splitURL(ci.Options.WebhookURL) | ||||
| 			isGlobalWebhook = false | ||||
| 		} | ||||
| 	// Handle prefix hint for unthreaded messages. | ||||
| 	if msg.ParentNotFound() { | ||||
| 		msg.ParentID = "" | ||||
| 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||
| 	} | ||||
| 	b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	// Use webhook to send the message | ||||
| 	if wID != "" && msg.Event != config.EventMsgDelete { | ||||
| 		// skip events | ||||
| 		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 { | ||||
| 				b.Log.Errorf("Could not delete edited webhook message: %s", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		b.Log.Debugf("Broadcasting using Webhook") | ||||
|  | ||||
| 		// skip empty messages | ||||
| 		if msg.Text == "" && (msg.Extra == nil || len(msg.Extra["file"]) == 0) { | ||||
| 			b.Log.Debugf("Skipping empty message %#v", msg) | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		msg.Text = helper.ClipMessage(msg.Text, MessageLength) | ||||
| 		msg.Text = b.replaceUserMentions(msg.Text) | ||||
| 		// discord username must be [0..32] max | ||||
| 		if len(msg.Username) > 32 { | ||||
| 			msg.Username = msg.Username[0:32] | ||||
| 		} | ||||
| 		// if we have a global webhook for this Discord account, and permission | ||||
| 		// to modify webhooks (previously verified), then set its channel to | ||||
| 		// the message channel before using it | ||||
| 		// TODO: this isn't necessary if the last message from this webhook was | ||||
| 		// sent to the current channel | ||||
| 		if isGlobalWebhook && b.canEditWebhooks { | ||||
| 			b.Log.Debugf("Setting webhook channel to \"%s\"", msg.Channel) | ||||
| 			_, err := b.c.WebhookEdit(wID, "", "", channelID) | ||||
| 			if err != nil { | ||||
| 				b.Log.Errorf("Could not set webhook channel: %s", err) | ||||
| 				return "", err | ||||
| 			} | ||||
| 		} | ||||
| 		b.Log.Debugf("Processing webhook sending for message %#v", msg) | ||||
| 		msg, err := b.webhookSend(&msg, wID, wToken) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("Could not broadcast via webook for message %#v: %s", msg, err) | ||||
| 			return "", err | ||||
| 		} | ||||
| 		if msg == nil { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		b.updateCacheID(origMsgID, msg.ID) | ||||
| 		return msg.ID, nil | ||||
| 	useWebhooks := b.shouldMessageUseWebhooks(&msg) | ||||
| 	if useWebhooks && msg.Event != config.EventMsgDelete && msg.ParentID == "" { | ||||
| 		return b.handleEventWebhook(&msg, channelID) | ||||
| 	} | ||||
|  | ||||
| 	return b.handleEventBotUser(&msg, channelID) | ||||
| } | ||||
|  | ||||
| // handleEventDirect handles events via the bot user | ||||
| func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (string, error) { | ||||
| 	b.Log.Debugf("Broadcasting using token (API)") | ||||
|  | ||||
| 	// Delete message | ||||
| @@ -291,14 +276,13 @@ 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 | ||||
| 	} | ||||
|  | ||||
| 	// Upload a file if it exists | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||
| 		for _, rmsg := range helper.HandleExtra(msg, b.General) { | ||||
| 			rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength) | ||||
| 			if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil { | ||||
| 				b.Log.Errorf("Could not send message %#v: %s", rmsg, err) | ||||
| @@ -306,7 +290,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 		} | ||||
| 		// check if we have files to upload (from slack, telegram or mattermost) | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			return b.handleUploadFile(&msg, channelID) | ||||
| 			return b.handleUploadFile(msg, channelID) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -319,54 +303,27 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { | ||||
| 		return msg.ID, err | ||||
| 	} | ||||
|  | ||||
| 	m := discordgo.MessageSend{ | ||||
| 		Content: msg.Username + msg.Text, | ||||
| 	} | ||||
|  | ||||
| 	if msg.ParentValid() { | ||||
| 		m.Reference = &discordgo.MessageReference{ | ||||
| 			MessageID: msg.ParentID, | ||||
| 			ChannelID: channelID, | ||||
| 			GuildID:   b.guildID, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Post normal message | ||||
| 	res, err := b.c.ChannelMessageSend(channelID, msg.Username+msg.Text) | ||||
| 	res, err := b.c.ChannelMessageSendComplex(channelID, &m) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	return res.ID, nil | ||||
| } | ||||
|  | ||||
| // useWebhook returns true if we have a webhook defined somewhere | ||||
| func (b *Bdiscord) useWebhook() bool { | ||||
| 	if b.GetString("WebhookURL") != "" { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	defer b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	for _, channel := range b.channelInfoMap { | ||||
| 		if channel.Options.WebhookURL != "" { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // isWebhookID returns true if the specified id is used in a defined webhook | ||||
| func (b *Bdiscord) isWebhookID(id string) bool { | ||||
| 	if b.GetString("WebhookURL") != "" { | ||||
| 		wID, _ := b.splitURL(b.GetString("WebhookURL")) | ||||
| 		if wID == id { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	defer b.channelsMutex.RUnlock() | ||||
|  | ||||
| 	for _, channel := range b.channelInfoMap { | ||||
| 		if channel.Options.WebhookURL != "" { | ||||
| 			wID, _ := b.splitURL(channel.Options.WebhookURL) | ||||
| 			if wID == id { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // handleUploadFile handles native upload of files | ||||
| func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) { | ||||
| 	var err error | ||||
| @@ -388,83 +345,3 @@ func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (stri | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| // webhookSend send one or more message via webhook, taking care of file | ||||
| // uploads (from slack, telegram or mattermost). | ||||
| // Returns messageID and error. | ||||
| func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*discordgo.Message, error) { | ||||
| 	var ( | ||||
| 		res *discordgo.Message | ||||
| 		err error | ||||
| 	) | ||||
|  | ||||
| 	// If avatar is unset, check if UseLocalAvatar contains the message's | ||||
| 	// account or protocol, and if so, try to find a local avatar | ||||
| 	if msg.Avatar == "" { | ||||
| 		for _, val := range b.GetStringSlice("UseLocalAvatar") { | ||||
| 			if msg.Protocol == val || msg.Account == val { | ||||
| 				if avatar := b.findAvatar(msg); avatar != "" { | ||||
| 					msg.Avatar = avatar | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// WebhookParams can have either `Content` or `File`. | ||||
|  | ||||
| 	// We can't send empty messages. | ||||
| 	if msg.Text != "" { | ||||
| 		res, err = b.c.WebhookExecute( | ||||
| 			webhookID, | ||||
| 			token, | ||||
| 			true, | ||||
| 			&discordgo.WebhookParams{ | ||||
| 				Content:   msg.Text, | ||||
| 				Username:  msg.Username, | ||||
| 				AvatarURL: msg.Avatar, | ||||
| 			}, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("Could not send text (%s) for message %#v: %s", msg.Text, msg, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, f := range msg.Extra["file"] { | ||||
| 			fi := f.(config.FileInfo) | ||||
| 			file := discordgo.File{ | ||||
| 				Name:        fi.Name, | ||||
| 				ContentType: "", | ||||
| 				Reader:      bytes.NewReader(*fi.Data), | ||||
| 			} | ||||
| 			content := "" | ||||
| 			if msg.Text == "" { | ||||
| 				content = fi.Comment | ||||
| 			} | ||||
| 			_, e2 := b.c.WebhookExecute( | ||||
| 				webhookID, | ||||
| 				token, | ||||
| 				false, | ||||
| 				&discordgo.WebhookParams{ | ||||
| 					Username:  msg.Username, | ||||
| 					AvatarURL: msg.Avatar, | ||||
| 					File:      &file, | ||||
| 					Content:   content, | ||||
| 				}, | ||||
| 			) | ||||
| 			if e2 != nil { | ||||
| 				b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, e2) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return res, err | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) findAvatar(m *config.Message) string { | ||||
| 	member, err := b.getGuildMemberByNick(m.Username) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return member.User.AvatarURL("") | ||||
| } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 		return | ||||
| 	} | ||||
| 	// if using webhooks, do not relay if it's ours | ||||
| 	if b.useWebhook() && m.Author.Bot && b.isWebhookID(m.Author.ID) { | ||||
| 	if m.Author.Bot && b.transmitter.HasWebhook(m.Author.ID) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @@ -127,6 +127,11 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat | ||||
| 	// Replace emotes | ||||
| 	rmsg.Text = replaceEmotes(rmsg.Text) | ||||
|  | ||||
| 	// Add our parent id if it exists, and if it's not referring to a message in another channel | ||||
| 	if ref := m.MessageReference; ref != nil && ref.ChannelID == m.ChannelID { | ||||
| 		rmsg.ParentID = ref.MessageID | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 	b.Remote <- rmsg | ||||
|   | ||||
| @@ -196,7 +196,7 @@ func (b *Bdiscord) replaceAction(text string) (string, bool) { | ||||
| } | ||||
|  | ||||
| // splitURL splits a webhookURL and returns the ID and token. | ||||
| func (b *Bdiscord) splitURL(url string) (string, string) { | ||||
| func (b *Bdiscord) splitURL(url string) (string, string, bool) { | ||||
| 	const ( | ||||
| 		expectedWebhookSplitCount = 7 | ||||
| 		webhookIdxID              = 5 | ||||
| @@ -204,43 +204,9 @@ func (b *Bdiscord) splitURL(url string) (string, string) { | ||||
| 	) | ||||
| 	webhookURLSplit := strings.Split(url, "/") | ||||
| 	if len(webhookURLSplit) != expectedWebhookSplitCount { | ||||
| 		b.Log.Fatalf("%s is no correct discord WebhookURL", url) | ||||
| 		return "", "", false | ||||
| 	} | ||||
| 	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() | ||||
| 	return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken], true | ||||
| } | ||||
|  | ||||
| func enumerateUsernames(s string) []string { | ||||
|   | ||||
							
								
								
									
										257
									
								
								bridge/discord/transmitter/transmitter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								bridge/discord/transmitter/transmitter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | ||||
| // Package transmitter provides functionality for transmitting | ||||
| // arbitrary webhook messages to Discord. | ||||
| // | ||||
| // The package provides the following functionality: | ||||
| // | ||||
| // - Creating new webhooks, whenever necessary | ||||
| // - Loading webhooks that we have previously created | ||||
| // - Sending new messages | ||||
| // - Editing messages, via message ID | ||||
| // - Deleting messages, via message ID | ||||
| // | ||||
| // The package has been designed for matterbridge, but with other | ||||
| // Go bots in mind. The public API should be matterbridge-agnostic. | ||||
| package transmitter | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // A Transmitter represents a message manager for a single guild. | ||||
| type Transmitter struct { | ||||
| 	session    *discordgo.Session | ||||
| 	guild      string | ||||
| 	title      string | ||||
| 	autoCreate bool | ||||
|  | ||||
| 	// channelWebhooks maps from a channel ID to a webhook instance | ||||
| 	channelWebhooks map[string]*discordgo.Webhook | ||||
|  | ||||
| 	mutex sync.RWMutex | ||||
|  | ||||
| 	Log *log.Entry | ||||
| } | ||||
|  | ||||
| // ErrWebhookNotFound is returned when a valid webhook for this channel/message combination does not exist | ||||
| var ErrWebhookNotFound = errors.New("webhook for this channel and message does not exist") | ||||
|  | ||||
| // ErrPermissionDenied is returned if the bot does not have permission to manage webhooks. | ||||
| // | ||||
| // Bots can be granted a guild-wide permission and channel-specific permissions to manage webhooks. | ||||
| // Despite potentially having guild-wide permission, channel specific overrides could deny a bot's permission to manage webhooks. | ||||
| var ErrPermissionDenied = errors.New("missing 'Manage Webhooks' permission") | ||||
|  | ||||
| // New returns a new Transmitter given a Discord session, guild ID, and title. | ||||
| func New(session *discordgo.Session, guild string, title string, autoCreate bool) *Transmitter { | ||||
| 	return &Transmitter{ | ||||
| 		session:    session, | ||||
| 		guild:      guild, | ||||
| 		title:      title, | ||||
| 		autoCreate: autoCreate, | ||||
|  | ||||
| 		channelWebhooks: make(map[string]*discordgo.Webhook), | ||||
|  | ||||
| 		Log: log.NewEntry(log.StandardLogger()), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Send transmits a message to the given channel with the provided webhook data, and waits until Discord responds with message data. | ||||
| func (t *Transmitter) Send(channelID string, params *discordgo.WebhookParams) (*discordgo.Message, error) { | ||||
| 	wh, err := t.getOrCreateWebhook(channelID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	msg, err := t.session.WebhookExecute(wh.ID, wh.Token, true, params) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("execute failed: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return msg, nil | ||||
| } | ||||
|  | ||||
| // Edit will edit a message in a channel, if possible. | ||||
| func (t *Transmitter) Edit(channelID string, messageID string, params *discordgo.WebhookParams) error { | ||||
| 	wh := t.getWebhook(channelID) | ||||
|  | ||||
| 	if wh == nil { | ||||
| 		return ErrWebhookNotFound | ||||
| 	} | ||||
|  | ||||
| 	uri := discordgo.EndpointWebhookToken(wh.ID, wh.Token) + "/messages/" + messageID | ||||
| 	_, err := t.session.RequestWithBucketID("PATCH", uri, params, discordgo.EndpointWebhookToken("", "")) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // HasWebhook checks whether the transmitter is using a particular webhook. | ||||
| func (t *Transmitter) HasWebhook(id string) bool { | ||||
| 	t.mutex.RLock() | ||||
| 	defer t.mutex.RUnlock() | ||||
|  | ||||
| 	for _, wh := range t.channelWebhooks { | ||||
| 		if wh.ID == id { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // AddWebhook allows you to register a channel's webhook with the transmitter. | ||||
| func (t *Transmitter) AddWebhook(channelID string, webhook *discordgo.Webhook) bool { | ||||
| 	t.Log.Debugf("Manually added webhook %#v to channel %#v", webhook.ID, channelID) | ||||
| 	t.mutex.Lock() | ||||
| 	defer t.mutex.Unlock() | ||||
|  | ||||
| 	_, replaced := t.channelWebhooks[channelID] | ||||
| 	t.channelWebhooks[channelID] = webhook | ||||
| 	return replaced | ||||
| } | ||||
|  | ||||
| // RefreshGuildWebhooks loads "relevant" webhooks into the transmitter, with careful permission handling. | ||||
| // | ||||
| // Notes: | ||||
| // | ||||
| // - A webhook is "relevant" if it was created by this bot -- the ApplicationID should match the bot's ID. | ||||
| // - The term "having permission" means having the "Manage Webhooks" permission. See ErrPermissionDenied for more information. | ||||
| // - This function is additive and will not unload previously loaded webhooks. | ||||
| // - A nil channelIDs slice is treated the same as an empty one. | ||||
| // | ||||
| // If the bot has guild-wide permission: | ||||
| // | ||||
| // 1. it will load any "relevant" webhooks from the entire guild | ||||
| // 2. the given slice is ignored | ||||
| // | ||||
| // If the bot does not have guild-wide permission: | ||||
| // | ||||
| // 1. it will load any "relevant" webhooks in each channel | ||||
| // 2. a single error will be returned if any error occurs (incl. if there is no permission for any of these channels) | ||||
| // | ||||
| // If any channel has more than one "relevant" webhook, it will randomly pick one. | ||||
| func (t *Transmitter) RefreshGuildWebhooks(channelIDs []string) error { | ||||
| 	t.Log.Debugln("Refreshing guild webhooks") | ||||
|  | ||||
| 	botID, err := getDiscordUserID(t.session) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not get current user: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// Get all existing webhooks | ||||
| 	hooks, err := t.session.GuildWebhooks(t.guild) | ||||
| 	if err != nil { | ||||
| 		switch { | ||||
| 		case isDiscordPermissionError(err): | ||||
| 			// We fallback on manually fetching hooks from individual channels | ||||
| 			// if we don't have the "Manage Webhooks" permission globally. | ||||
| 			// We can only do this if we were provided channelIDs, though. | ||||
| 			if len(channelIDs) == 0 { | ||||
| 				return ErrPermissionDenied | ||||
| 			} | ||||
| 			t.Log.Debugln("Missing global 'Manage Webhooks' permission, falling back on per-channel permission") | ||||
| 			return t.fetchChannelsHooks(channelIDs, botID) | ||||
| 		default: | ||||
| 			return fmt.Errorf("could not get webhooks: %w", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	t.Log.Debugln("Refreshing guild webhooks using global permission") | ||||
| 	t.assignHooksByAppID(hooks, botID, false) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // createWebhook creates a webhook for a specific channel. | ||||
| func (t *Transmitter) createWebhook(channel string) (*discordgo.Webhook, error) { | ||||
| 	t.mutex.Lock() | ||||
| 	defer t.mutex.Unlock() | ||||
|  | ||||
| 	wh, err := t.session.WebhookCreate(channel, t.title+time.Now().Format(" 3:04:05PM"), "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	t.channelWebhooks[channel] = wh | ||||
| 	return wh, nil | ||||
| } | ||||
|  | ||||
| func (t *Transmitter) getWebhook(channel string) *discordgo.Webhook { | ||||
| 	t.mutex.RLock() | ||||
| 	defer t.mutex.RUnlock() | ||||
|  | ||||
| 	return t.channelWebhooks[channel] | ||||
| } | ||||
|  | ||||
| func (t *Transmitter) getOrCreateWebhook(channelID string) (*discordgo.Webhook, error) { | ||||
| 	// If we have a webhook for this channel, immediately return it | ||||
| 	wh := t.getWebhook(channelID) | ||||
| 	if wh != nil { | ||||
| 		return wh, nil | ||||
| 	} | ||||
|  | ||||
| 	// Early exit if we don't want to automatically create one | ||||
| 	if !t.autoCreate { | ||||
| 		return nil, ErrWebhookNotFound | ||||
| 	} | ||||
|  | ||||
| 	t.Log.Infof("Creating a webhook for %s\n", channelID) | ||||
| 	wh, err := t.createWebhook(channelID) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not create webhook: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return wh, nil | ||||
| } | ||||
|  | ||||
| // fetchChannelsHooks fetches hooks for the given channelIDs and calls assignHooksByAppID for each channel's hooks | ||||
| func (t *Transmitter) fetchChannelsHooks(channelIDs []string, botID string) error { | ||||
| 	// For each channel, search for relevant hooks | ||||
| 	var failedHooks []string | ||||
| 	for _, channelID := range channelIDs { | ||||
| 		hooks, err := t.session.ChannelWebhooks(channelID) | ||||
| 		if err != nil { | ||||
| 			failedHooks = append(failedHooks, "\n- "+channelID+": "+err.Error()) | ||||
| 			continue | ||||
| 		} | ||||
| 		t.assignHooksByAppID(hooks, botID, true) | ||||
| 	} | ||||
|  | ||||
| 	// Compose an error if any hooks failed | ||||
| 	if len(failedHooks) > 0 { | ||||
| 		return errors.New("failed to fetch hooks:" + strings.Join(failedHooks, "")) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (t *Transmitter) assignHooksByAppID(hooks []*discordgo.Webhook, appID string, channelTargeted bool) { | ||||
| 	logLine := "Picking up webhook" | ||||
| 	if channelTargeted { | ||||
| 		logLine += " (channel targeted)" | ||||
| 	} | ||||
|  | ||||
| 	t.mutex.Lock() | ||||
| 	defer t.mutex.Unlock() | ||||
|  | ||||
| 	for _, wh := range hooks { | ||||
| 		if wh.ApplicationID != appID { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		t.channelWebhooks[wh.ChannelID] = wh | ||||
| 		t.Log.WithFields(log.Fields{ | ||||
| 			"id":      wh.ID, | ||||
| 			"name":    wh.Name, | ||||
| 			"channel": wh.ChannelID, | ||||
| 		}).Println(logLine) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										32
									
								
								bridge/discord/transmitter/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								bridge/discord/transmitter/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| package transmitter | ||||
|  | ||||
| import ( | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| ) | ||||
|  | ||||
| // isDiscordPermissionError returns false for nil, and true if a Discord RESTError with code discordgo.ErrorCodeMissionPermissions | ||||
| func isDiscordPermissionError(err error) bool { | ||||
| 	if err == nil { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	restErr, ok := err.(*discordgo.RESTError) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	return restErr.Message != nil && restErr.Message.Code == discordgo.ErrCodeMissingPermissions | ||||
| } | ||||
|  | ||||
| // getDiscordUserID gets own user ID from state, and fallback on API request | ||||
| func getDiscordUserID(session *discordgo.Session) (string, error) { | ||||
| 	if user := session.State.User; user != nil { | ||||
| 		return user.ID, nil | ||||
| 	} | ||||
|  | ||||
| 	user, err := session.User("@me") | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return user.ID, nil | ||||
| } | ||||
							
								
								
									
										147
									
								
								bridge/discord/webhook.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								bridge/discord/webhook.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| package bdiscord | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/matterbridge/discordgo" | ||||
| ) | ||||
|  | ||||
| // shouldMessageUseWebhooks checks if have a channel specific webhook, if we're not using auto webhooks | ||||
| func (b *Bdiscord) shouldMessageUseWebhooks(msg *config.Message) bool { | ||||
| 	if b.useAutoWebhooks { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	b.channelsMutex.RLock() | ||||
| 	defer b.channelsMutex.RUnlock() | ||||
| 	if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok { | ||||
| 		if ci.Options.WebhookURL != "" { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // maybeGetLocalAvatar checks if UseLocalAvatar contains the message's | ||||
| // account or protocol, and if so, returns the Discord avatar (if exists) | ||||
| func (b *Bdiscord) maybeGetLocalAvatar(msg *config.Message) string { | ||||
| 	for _, val := range b.GetStringSlice("UseLocalAvatar") { | ||||
| 		if msg.Protocol != val && msg.Account != val { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		member, err := b.getGuildMemberByNick(msg.Username) | ||||
| 		if err != nil { | ||||
| 			return "" | ||||
| 		} | ||||
|  | ||||
| 		return member.User.AvatarURL("") | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // webhookSend send one or more message via webhook, taking care of file | ||||
| // uploads (from slack, telegram or mattermost). | ||||
| // Returns messageID and error. | ||||
| func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) { | ||||
| 	var ( | ||||
| 		res *discordgo.Message | ||||
| 		err error | ||||
| 	) | ||||
|  | ||||
| 	// If avatar is unset, mutate the message to include the local avatar (but only if settings say we should do this) | ||||
| 	if msg.Avatar == "" { | ||||
| 		msg.Avatar = b.maybeGetLocalAvatar(msg) | ||||
| 	} | ||||
|  | ||||
| 	// WebhookParams can have either `Content` or `File`. | ||||
|  | ||||
| 	// We can't send empty messages. | ||||
| 	if msg.Text != "" { | ||||
| 		res, err = b.transmitter.Send( | ||||
| 			channelID, | ||||
| 			&discordgo.WebhookParams{ | ||||
| 				Content:   msg.Text, | ||||
| 				Username:  msg.Username, | ||||
| 				AvatarURL: msg.Avatar, | ||||
| 			}, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("Could not send text (%s) for message %#v: %s", msg.Text, msg, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, f := range msg.Extra["file"] { | ||||
| 			fi := f.(config.FileInfo) | ||||
| 			file := discordgo.File{ | ||||
| 				Name:        fi.Name, | ||||
| 				ContentType: "", | ||||
| 				Reader:      bytes.NewReader(*fi.Data), | ||||
| 			} | ||||
| 			content := "" | ||||
| 			if msg.Text == "" { | ||||
| 				content = fi.Comment | ||||
| 			} | ||||
| 			_, e2 := b.transmitter.Send( | ||||
| 				channelID, | ||||
| 				&discordgo.WebhookParams{ | ||||
| 					Username:  msg.Username, | ||||
| 					AvatarURL: msg.Avatar, | ||||
| 					File:      &file, | ||||
| 					Content:   content, | ||||
| 				}, | ||||
| 			) | ||||
| 			if e2 != nil { | ||||
| 				b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, e2) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return res, err | ||||
| } | ||||
|  | ||||
| func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (string, error) { | ||||
| 	// skip events | ||||
| 	if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	// skip empty messages | ||||
| 	if msg.Text == "" && (msg.Extra == nil || len(msg.Extra["file"]) == 0) { | ||||
| 		b.Log.Debugf("Skipping empty message %#v", msg) | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	msg.Text = helper.ClipMessage(msg.Text, MessageLength) | ||||
| 	msg.Text = b.replaceUserMentions(msg.Text) | ||||
| 	// discord username must be [0..32] max | ||||
| 	if len(msg.Username) > 32 { | ||||
| 		msg.Username = msg.Username[0:32] | ||||
| 	} | ||||
|  | ||||
| 	if msg.ID != "" { | ||||
| 		b.Log.Debugf("Editing webhook message") | ||||
| 		err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{ | ||||
| 			Content:  msg.Text, | ||||
| 			Username: msg.Username, | ||||
| 		}) | ||||
| 		if err == nil { | ||||
| 			return msg.ID, nil | ||||
| 		} | ||||
| 		b.Log.Errorf("Could not edit webhook message: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("Processing webhook sending for message %#v", msg) | ||||
| 	discordMsg, err := b.webhookSend(msg, channelID) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msg, err) | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if discordMsg == nil { | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	return discordMsg.ID, nil | ||||
| } | ||||
| @@ -5,7 +5,10 @@ import ( | ||||
| 	"fmt" | ||||
| 	"image/png" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| @@ -48,6 +51,30 @@ func DownloadFileAuth(url string, auth string) (*[]byte, error) { | ||||
| 	return &data, nil | ||||
| } | ||||
|  | ||||
| // DownloadFileAuthRocket downloads the given URL using the specified Rocket user ID and authentication token. | ||||
| func DownloadFileAuthRocket(url, token, userID string) (*[]byte, error) { | ||||
| 	var buf bytes.Buffer | ||||
| 	client := &http.Client{ | ||||
| 		Timeout: time.Second * 5, | ||||
| 	} | ||||
| 	req, err := http.NewRequest("GET", url, nil) | ||||
|  | ||||
| 	req.Header.Add("X-Auth-Token", token) | ||||
| 	req.Header.Add("X-User-Id", userID) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	_, err = io.Copy(&buf, resp.Body) | ||||
| 	data := buf.Bytes() | ||||
| 	return &data, err | ||||
| } | ||||
|  | ||||
| // GetSubLines splits messages in newline-delimited lines. If maxLineLength is | ||||
| // specified as non-zero GetSubLines will also clip long lines to the maximum | ||||
| // length and insert a warning marker that the line was clipped. | ||||
| @@ -180,7 +207,7 @@ func ClipMessage(text string, length int) string { | ||||
|  | ||||
| // ParseMarkdown takes in an input string as markdown and parses it to html | ||||
| func ParseMarkdown(input string) string { | ||||
| 	extensions := parser.HardLineBreak | parser.NoIntraEmphasis | ||||
| 	extensions := parser.HardLineBreak | parser.NoIntraEmphasis | parser.FencedCode | ||||
| 	markdownParser := parser.NewWithExtensions(extensions) | ||||
| 	renderer := html.NewRenderer(html.RendererOptions{ | ||||
| 		Flags: 0, | ||||
| @@ -192,7 +219,7 @@ func ParseMarkdown(input string) string { | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // ConvertWebPToPNG convert input data (which should be WebP format to PNG format) | ||||
| // ConvertWebPToPNG converts input data (which should be WebP format) to PNG format | ||||
| func ConvertWebPToPNG(data *[]byte) error { | ||||
| 	r := bytes.NewReader(*data) | ||||
| 	m, err := webp.Decode(r) | ||||
| @@ -207,3 +234,66 @@ func ConvertWebPToPNG(data *[]byte) error { | ||||
| 	*data = w.Bytes() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // CanConvertTgsToX Checks whether the external command necessary for ConvertTgsToX works. | ||||
| func CanConvertTgsToX() error { | ||||
| 	// We depend on the fact that `lottie_convert.py --help` has exit status 0. | ||||
| 	// Hyrum's Law predicted this, and Murphy's Law predicts that this will break eventually. | ||||
| 	// However, there is no alternative like `lottie_convert.py --is-properly-installed` | ||||
| 	cmd := exec.Command("lottie_convert.py", "--help") | ||||
| 	return cmd.Run() | ||||
| } | ||||
|  | ||||
| // ConvertTgsToWebP convert input data (which should be tgs format) to WebP format | ||||
| // This relies on an external command, which is ugly, but works. | ||||
| func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error { | ||||
| 	// lottie can't handle input from a pipe, so write to a temporary file: | ||||
| 	tmpInFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-input-*.tgs") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	tmpInFileName := tmpInFile.Name() | ||||
| 	defer func() { | ||||
| 		if removeErr := os.Remove(tmpInFileName); removeErr != nil { | ||||
| 			logger.Errorf("Could not delete temporary (input) file %s: %v", tmpInFileName, removeErr) | ||||
| 		} | ||||
| 	}() | ||||
| 	// lottie can handle writing to a pipe, but there is no way to do that platform-independently. | ||||
| 	// "/dev/stdout" won't work on Windows, and "-" upsets Cairo for some reason. So we need another file: | ||||
| 	tmpOutFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-output-*.data") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	tmpOutFileName := tmpOutFile.Name() | ||||
| 	defer func() { | ||||
| 		if removeErr := os.Remove(tmpOutFileName); removeErr != nil { | ||||
| 			logger.Errorf("Could not delete temporary (output) file %s: %v", tmpOutFileName, removeErr) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	if _, writeErr := tmpInFile.Write(*data); writeErr != nil { | ||||
| 		return writeErr | ||||
| 	} | ||||
| 	// Must close before calling lottie to avoid data races: | ||||
| 	if closeErr := tmpInFile.Close(); closeErr != nil { | ||||
| 		return closeErr | ||||
| 	} | ||||
|  | ||||
| 	// Call lottie to transform: | ||||
| 	cmd := exec.Command("lottie_convert.py", "--input-format", "lottie", "--output-format", outputFormat, tmpInFileName, tmpOutFileName) | ||||
| 	cmd.Stdout = nil | ||||
| 	cmd.Stderr = nil | ||||
| 	// NB: lottie writes progress into to stderr in all cases. | ||||
| 	_, stderr := cmd.Output() | ||||
| 	if stderr != nil { | ||||
| 		// 'stderr' already contains some parts of Stderr, because it was set to 'nil'. | ||||
| 		return stderr | ||||
| 	} | ||||
| 	dataContents, err := ioutil.ReadFile(tmpOutFileName) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	*data = dataContents | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -67,6 +67,20 @@ func (b *Birc) handleFiles(msg *config.Message) bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleInvite(client *girc.Client, event girc.Event) { | ||||
| 	if len(event.Params) != 2 { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	channel := event.Params[1] | ||||
|  | ||||
| 	b.Log.Debugf("got invite for %s", channel) | ||||
|  | ||||
| 	if _, ok := b.channels[channel]; ok { | ||||
| 		b.i.Cmd.Join(channel) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) { | ||||
| 	if len(event.Params) == 0 { | ||||
| 		b.Log.Debugf("handleJoinPart: empty Params? %#v", event) | ||||
| @@ -109,14 +123,15 @@ func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) { | ||||
| 	i := b.i | ||||
| 	b.Nick = event.Params[0] | ||||
|  | ||||
| 	i.Handlers.Add("PRIVMSG", b.handlePrivMsg) | ||||
| 	i.Handlers.Add("CTCP_ACTION", b.handlePrivMsg) | ||||
| 	i.Handlers.AddBg("PRIVMSG", b.handlePrivMsg) | ||||
| 	i.Handlers.AddBg("CTCP_ACTION", b.handlePrivMsg) | ||||
| 	i.Handlers.Add(girc.RPL_TOPICWHOTIME, b.handleTopicWhoTime) | ||||
| 	i.Handlers.Add(girc.NOTICE, b.handleNotice) | ||||
| 	i.Handlers.Add("JOIN", b.handleJoinPart) | ||||
| 	i.Handlers.Add("PART", b.handleJoinPart) | ||||
| 	i.Handlers.Add("QUIT", b.handleJoinPart) | ||||
| 	i.Handlers.Add("KICK", b.handleJoinPart) | ||||
| 	i.Handlers.AddBg(girc.NOTICE, b.handleNotice) | ||||
| 	i.Handlers.AddBg("JOIN", b.handleJoinPart) | ||||
| 	i.Handlers.AddBg("PART", b.handleJoinPart) | ||||
| 	i.Handlers.AddBg("QUIT", b.handleJoinPart) | ||||
| 	i.Handlers.AddBg("KICK", b.handleJoinPart) | ||||
| 	i.Handlers.Add("INVITE", b.handleInvite) | ||||
| } | ||||
|  | ||||
| func (b *Birc) handleNickServ() { | ||||
| @@ -170,7 +185,14 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | ||||
| 	if b.skipPrivMsg(event) { | ||||
| 		return | ||||
| 	} | ||||
| 	rmsg := config.Message{Username: event.Source.Name, Channel: strings.ToLower(event.Params[0]), Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host} | ||||
|  | ||||
| 	rmsg := config.Message{ | ||||
| 		Username: event.Source.Name, | ||||
| 		Channel:  strings.ToLower(event.Params[0]), | ||||
| 		Account:  b.Account, | ||||
| 		UserID:   event.Source.Ident + "@" + event.Source.Host, | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("== Receiving PRIVMSG: %s %s %#v", event.Source.Name, event.Last(), event) | ||||
|  | ||||
| 	// set action event | ||||
| @@ -178,6 +200,11 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { | ||||
| 		rmsg.Event = config.EventUserAction | ||||
| 	} | ||||
|  | ||||
| 	// set NOTICE event | ||||
| 	if event.Command == "NOTICE" { | ||||
| 		rmsg.Event = config.EventNoticeIRC | ||||
| 	} | ||||
|  | ||||
| 	// strip action, we made an event if it was an action | ||||
| 	rmsg.Text += event.StripAction() | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"hash/crc32" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| @@ -29,6 +30,7 @@ type Birc struct { | ||||
| 	Local                                     chan config.Message // local queue for flood control | ||||
| 	FirstConnection, authDone                 bool | ||||
| 	MessageDelay, MessageQueue, MessageLength int | ||||
| 	channels                                  map[string]bool | ||||
|  | ||||
| 	*bridge.Config | ||||
| } | ||||
| @@ -39,6 +41,8 @@ func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	b.Nick = b.GetString("Nick") | ||||
| 	b.names = make(map[string][]string) | ||||
| 	b.connected = make(chan error) | ||||
| 	b.channels = make(map[string]bool) | ||||
|  | ||||
| 	if b.GetInt("MessageDelay") == 0 { | ||||
| 		b.MessageDelay = 1300 | ||||
| 	} else { | ||||
| @@ -111,6 +115,7 @@ func (b *Birc) Disconnect() error { | ||||
| } | ||||
|  | ||||
| func (b *Birc) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	b.channels[channel.Name] = true | ||||
| 	// need to check if we have nickserv auth done before joining channels | ||||
| 	for { | ||||
| 		if b.authDone { | ||||
| @@ -200,22 +205,58 @@ func (b *Birc) doConnect() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Sanitize nicks for RELAYMSG: replace IRC characters with special meanings with "-" | ||||
| func sanitizeNick(nick string) string { | ||||
| 	sanitize := func(r rune) rune { | ||||
| 		if strings.ContainsRune("!+%@&#$:'\"?*,. ", r) { | ||||
| 			return '-' | ||||
| 		} | ||||
| 		return r | ||||
| 	} | ||||
| 	return strings.Map(sanitize, nick) | ||||
| } | ||||
|  | ||||
| func (b *Birc) doSend() { | ||||
| 	rate := time.Millisecond * time.Duration(b.MessageDelay) | ||||
| 	throttle := time.NewTicker(rate) | ||||
| 	for msg := range b.Local { | ||||
| 		<-throttle.C | ||||
| 		username := msg.Username | ||||
| 		if b.GetBool("Colornicks") { | ||||
| 			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) | ||||
| 		} | ||||
| 		if msg.Event == config.EventUserAction { | ||||
| 			b.i.Cmd.Action(msg.Channel, username+msg.Text) | ||||
| 		// Optional support for the proposed RELAYMSG extension, described at | ||||
| 		// https://github.com/jlu5/ircv3-specifications/blob/master/extensions/relaymsg.md | ||||
| 		// nolint:nestif | ||||
| 		if (b.i.HasCapability("overdrivenetworks.com/relaymsg") || b.i.HasCapability("draft/relaymsg")) && | ||||
| 			b.GetBool("UseRelayMsg") { | ||||
| 			username = sanitizeNick(username) | ||||
| 			text := msg.Text | ||||
|  | ||||
| 			// Work around girc chomping leading commas on single word messages? | ||||
| 			if strings.HasPrefix(text, ":") && !strings.ContainsRune(text, ' ') { | ||||
| 				text = ":" + text | ||||
| 			} | ||||
|  | ||||
| 			if msg.Event == config.EventUserAction { | ||||
| 				b.i.Cmd.SendRawf("RELAYMSG %s %s :\x01ACTION %s\x01", msg.Channel, username, text) //nolint:errcheck | ||||
| 			} else { | ||||
| 				b.Log.Debugf("Sending RELAYMSG to channel %s: nick=%s", msg.Channel, username) | ||||
| 				b.i.Cmd.SendRawf("RELAYMSG %s %s :%s", msg.Channel, username, text) //nolint:errcheck | ||||
| 			} | ||||
| 		} else { | ||||
| 			b.Log.Debugf("Sending to channel %s", msg.Channel) | ||||
| 			b.i.Cmd.Message(msg.Channel, username+msg.Text) | ||||
| 			if b.GetBool("Colornicks") { | ||||
| 				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) | ||||
| 			} | ||||
| 			switch msg.Event { | ||||
| 			case config.EventUserAction: | ||||
| 				b.i.Cmd.Action(msg.Channel, username+msg.Text) | ||||
| 			case config.EventNoticeIRC: | ||||
| 				b.Log.Debugf("Sending notice to channel %s", msg.Channel) | ||||
| 				b.i.Cmd.Notice(msg.Channel, username+msg.Text) | ||||
| 			default: | ||||
| 				b.Log.Debugf("Sending to channel %s", msg.Channel) | ||||
| 				b.i.Cmd.Message(msg.Channel, username+msg.Text) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -240,6 +281,18 @@ func (b *Birc) getClient() (*girc.Client, error) { | ||||
| 		user = user[1:] | ||||
| 	} | ||||
|  | ||||
| 	debug := ioutil.Discard | ||||
| 	if b.GetInt("DebugLevel") == 2 { | ||||
| 		debug = b.Log.Writer() | ||||
| 	} | ||||
|  | ||||
| 	pingDelay, err := time.ParseDuration(b.GetString("pingdelay")) | ||||
| 	if err != nil || pingDelay == 0 { | ||||
| 		pingDelay = time.Minute | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("setting pingdelay to %s", pingDelay) | ||||
|  | ||||
| 	i := girc.New(girc.Config{ | ||||
| 		Server:     server, | ||||
| 		ServerPass: b.GetString("Password"), | ||||
| @@ -249,7 +302,11 @@ func (b *Birc) getClient() (*girc.Client, error) { | ||||
| 		Name:       b.GetString("Nick"), | ||||
| 		SSL:        b.GetBool("UseTLS"), | ||||
| 		TLSConfig:  &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec | ||||
| 		PingDelay:  time.Minute, | ||||
| 		PingDelay:  pingDelay, | ||||
| 		// skip gIRC internal rate limiting, since we have our own throttling | ||||
| 		AllowFlood:    true, | ||||
| 		Debug:         debug, | ||||
| 		SupportedCaps: map[string][]string{"overdrivenetworks.com/relaymsg": nil, "draft/relaymsg": nil}, | ||||
| 	}) | ||||
| 	return i, nil | ||||
| } | ||||
| @@ -275,7 +332,7 @@ func (b *Birc) skipPrivMsg(event girc.Event) bool { | ||||
| 	b.Nick = b.i.GetNick() | ||||
|  | ||||
| 	// freenode doesn't send 001 as first reply | ||||
| 	if event.Command == "NOTICE" { | ||||
| 	if event.Command == "NOTICE" && len(event.Params) != 2 { | ||||
| 		return true | ||||
| 	} | ||||
| 	// don't forward queries to the bot | ||||
| @@ -286,6 +343,15 @@ func (b *Birc) skipPrivMsg(event girc.Event) bool { | ||||
| 	if event.Source.Name == b.Nick { | ||||
| 		return true | ||||
| 	} | ||||
| 	// don't forward messages we sent via RELAYMSG | ||||
| 	if relayedNick, ok := event.Tags.Get("draft/relaymsg"); ok && relayedNick == b.Nick { | ||||
| 		return true | ||||
| 	} | ||||
| 	// This is the old name of the cap sent in spoofed messages; I've kept this in | ||||
| 	// for compatibility reasons | ||||
| 	if relayedNick, ok := event.Tags.Get("relaymsg"); ok && relayedNick == b.Nick { | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										215
									
								
								bridge/matrix/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								bridge/matrix/helpers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| package bmatrix | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"html" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	matrix "github.com/matrix-org/gomatrix" | ||||
| ) | ||||
|  | ||||
| func newMatrixUsername(username string) *matrixUsername { | ||||
| 	mUsername := new(matrixUsername) | ||||
|  | ||||
| 	// check if we have a </tag>. if we have, we don't escape HTML. #696 | ||||
| 	if htmlTag.MatchString(username) { | ||||
| 		mUsername.formatted = username | ||||
| 		// remove the HTML formatting for beautiful push messages #1188 | ||||
| 		mUsername.plain = htmlReplacementTag.ReplaceAllString(username, "") | ||||
| 	} else { | ||||
| 		mUsername.formatted = html.EscapeString(username) | ||||
| 		mUsername.plain = username | ||||
| 	} | ||||
|  | ||||
| 	return mUsername | ||||
| } | ||||
|  | ||||
| // getRoomID retrieves a matching room ID from the channel name. | ||||
| func (b *Bmatrix) getRoomID(channel string) string { | ||||
| 	b.RLock() | ||||
| 	defer b.RUnlock() | ||||
| 	for ID, name := range b.RoomMap { | ||||
| 		if name == channel { | ||||
| 			return ID | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // interface2Struct marshals and immediately unmarshals an interface. | ||||
| // Useful for converting map[string]interface{} to a struct. | ||||
| func interface2Struct(in interface{}, out interface{}) error { | ||||
| 	jsonObj, err := json.Marshal(in) | ||||
| 	if err != nil { | ||||
| 		return err //nolint:wrapcheck | ||||
| 	} | ||||
|  | ||||
| 	return json.Unmarshal(jsonObj, out) | ||||
| } | ||||
|  | ||||
| // getDisplayName retrieves the displayName for mxid, querying the homserver if the mxid is not in the cache. | ||||
| func (b *Bmatrix) getDisplayName(mxid string) string { | ||||
| 	if b.GetBool("UseUserName") { | ||||
| 		return mxid[1:] | ||||
| 	} | ||||
|  | ||||
| 	b.RLock() | ||||
| 	if val, present := b.NicknameMap[mxid]; present { | ||||
| 		b.RUnlock() | ||||
|  | ||||
| 		return val.displayName | ||||
| 	} | ||||
| 	b.RUnlock() | ||||
|  | ||||
| 	displayName, err := b.mc.GetDisplayName(mxid) | ||||
| 	var httpError *matrix.HTTPError | ||||
| 	if errors.As(err, &httpError) { | ||||
| 		b.Log.Warnf("Couldn't retrieve the display name for %s", mxid) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return b.cacheDisplayName(mxid, mxid[1:]) | ||||
| 	} | ||||
|  | ||||
| 	return b.cacheDisplayName(mxid, displayName.DisplayName) | ||||
| } | ||||
|  | ||||
| // cacheDisplayName stores the mapping between a mxid and a display name, to be reused later without performing a query to the homserver. | ||||
| // Note that old entries are cleaned when this function is called. | ||||
| func (b *Bmatrix) cacheDisplayName(mxid string, displayName string) string { | ||||
| 	now := time.Now() | ||||
|  | ||||
| 	// scan to delete old entries, to stop memory usage from becoming too high with old entries. | ||||
| 	// In addition, we also detect if another user have the same username, and if so, we append their mxids to their usernames to differentiate them. | ||||
| 	toDelete := []string{} | ||||
| 	conflict := false | ||||
|  | ||||
| 	b.Lock() | ||||
| 	for mxid, v := range b.NicknameMap { | ||||
| 		// to prevent username reuse across matrix servers - or even on the same server, append | ||||
| 		// the mxid to the username when there is a conflict | ||||
| 		if v.displayName == displayName { | ||||
| 			conflict = true | ||||
| 			// TODO: it would be nice to be able to rename previous messages from this user. | ||||
| 			// The current behavior is that only users with clashing usernames and *that have spoken since the bridge last started* will get their mxids shown, and I don't know if that's the expected behavior. | ||||
| 			v.displayName = fmt.Sprintf("%s (%s)", displayName, mxid) | ||||
| 			b.NicknameMap[mxid] = v | ||||
| 		} | ||||
|  | ||||
| 		if now.Sub(v.lastUpdated) > 10*time.Minute { | ||||
| 			toDelete = append(toDelete, mxid) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if conflict { | ||||
| 		displayName = fmt.Sprintf("%s (%s)", displayName, mxid) | ||||
| 	} | ||||
|  | ||||
| 	for _, v := range toDelete { | ||||
| 		delete(b.NicknameMap, v) | ||||
| 	} | ||||
|  | ||||
| 	b.NicknameMap[mxid] = NicknameCacheEntry{ | ||||
| 		displayName: displayName, | ||||
| 		lastUpdated: now, | ||||
| 	} | ||||
| 	b.Unlock() | ||||
|  | ||||
| 	return displayName | ||||
| } | ||||
|  | ||||
| // handleError converts errors into httpError. | ||||
| //nolint:exhaustivestruct | ||||
| func handleError(err error) *httpError { | ||||
| 	var mErr matrix.HTTPError | ||||
| 	if !errors.As(err, &mErr) { | ||||
| 		return &httpError{ | ||||
| 			Err: "not a HTTPError", | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var httpErr httpError | ||||
|  | ||||
| 	if err := json.Unmarshal(mErr.Contents, &httpErr); err != nil { | ||||
| 		return &httpError{ | ||||
| 			Err: "unmarshal failed", | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &httpErr | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool { | ||||
| 	// Skip empty messages | ||||
| 	if content["msgtype"] == nil { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// Only allow image,video or file msgtypes | ||||
| 	if !(content["msgtype"].(string) == "m.image" || | ||||
| 		content["msgtype"].(string) == "m.video" || | ||||
| 		content["msgtype"].(string) == "m.file") { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // getAvatarURL returns the avatar URL of the specified sender. | ||||
| func (b *Bmatrix) getAvatarURL(sender string) string { | ||||
| 	urlPath := b.mc.BuildURL("profile", sender, "avatar_url") | ||||
|  | ||||
| 	s := struct { | ||||
| 		AvatarURL string `json:"avatar_url"` | ||||
| 	}{} | ||||
|  | ||||
| 	err := b.mc.MakeRequest("GET", urlPath, nil, &s) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("getAvatarURL failed: %s", err) | ||||
|  | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	url := strings.ReplaceAll(s.AvatarURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/") | ||||
| 	if url != "" { | ||||
| 		url += "?width=37&height=37&method=crop" | ||||
| 	} | ||||
|  | ||||
| 	return url | ||||
| } | ||||
|  | ||||
| // handleRatelimit handles the ratelimit errors and return if we're ratelimited and the amount of time to sleep | ||||
| func (b *Bmatrix) handleRatelimit(err error) (time.Duration, bool) { | ||||
| 	httpErr := handleError(err) | ||||
| 	if httpErr.Errcode != "M_LIMIT_EXCEEDED" { | ||||
| 		return 0, false | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("ratelimited: %s", httpErr.Err) | ||||
| 	b.Log.Infof("getting ratelimited by matrix, sleeping approx %d seconds before retrying", httpErr.RetryAfterMs/1000) | ||||
|  | ||||
| 	return time.Duration(httpErr.RetryAfterMs) * time.Millisecond, true | ||||
| } | ||||
|  | ||||
| // retry function will check if we're ratelimited and retries again when backoff time expired | ||||
| // returns original error if not 429 ratelimit | ||||
| func (b *Bmatrix) retry(f func() error) error { | ||||
| 	b.rateMutex.Lock() | ||||
| 	defer b.rateMutex.Unlock() | ||||
|  | ||||
| 	for { | ||||
| 		if err := f(); err != nil { | ||||
| 			if backoff, ok := b.handleRatelimit(err); ok { | ||||
| 				time.Sleep(backoff) | ||||
| 			} else { | ||||
| 				return err | ||||
| 			} | ||||
| 		} else { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -3,31 +3,72 @@ package bmatrix | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"html" | ||||
| 	"mime" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	matrix "github.com/matterbridge/gomatrix" | ||||
| 	matrix "github.com/matrix-org/gomatrix" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	htmlTag            = regexp.MustCompile("</.*?>") | ||||
| 	htmlReplacementTag = regexp.MustCompile("<[^>]*>") | ||||
| ) | ||||
|  | ||||
| type NicknameCacheEntry struct { | ||||
| 	displayName string | ||||
| 	lastUpdated time.Time | ||||
| } | ||||
|  | ||||
| type Bmatrix struct { | ||||
| 	mc      *matrix.Client | ||||
| 	UserID  string | ||||
| 	RoomMap map[string]string | ||||
| 	mc          *matrix.Client | ||||
| 	UserID      string | ||||
| 	NicknameMap map[string]NicknameCacheEntry | ||||
| 	RoomMap     map[string]string | ||||
| 	rateMutex   sync.RWMutex | ||||
| 	sync.RWMutex | ||||
| 	htmlTag *regexp.Regexp | ||||
| 	*bridge.Config | ||||
| } | ||||
|  | ||||
| type httpError struct { | ||||
| 	Errcode      string `json:"errcode"` | ||||
| 	Err          string `json:"error"` | ||||
| 	RetryAfterMs int    `json:"retry_after_ms"` | ||||
| } | ||||
|  | ||||
| type matrixUsername struct { | ||||
| 	plain     string | ||||
| 	formatted string | ||||
| } | ||||
|  | ||||
| // SubTextMessage represents the new content of the message in edit messages. | ||||
| type SubTextMessage struct { | ||||
| 	MsgType string `json:"msgtype"` | ||||
| 	Body    string `json:"body"` | ||||
| } | ||||
|  | ||||
| // MessageRelation explains how the current message relates to a previous message. | ||||
| // Notably used for message edits. | ||||
| type MessageRelation struct { | ||||
| 	EventID string `json:"event_id"` | ||||
| 	Type    string `json:"rel_type"` | ||||
| } | ||||
|  | ||||
| type EditedMessage struct { | ||||
| 	NewContent SubTextMessage  `json:"m.new_content"` | ||||
| 	RelatedTo  MessageRelation `json:"m.relates_to"` | ||||
| 	matrix.TextMessage | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	b := &Bmatrix{Config: cfg} | ||||
| 	b.htmlTag = regexp.MustCompile("</.*?>") | ||||
| 	b.RoomMap = make(map[string]string) | ||||
| 	b.NicknameMap = make(map[string]NicknameCacheEntry) | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| @@ -39,9 +80,10 @@ func (b *Bmatrix) Connect() error { | ||||
| 		return err | ||||
| 	} | ||||
| 	resp, err := b.mc.Login(&matrix.ReqLogin{ | ||||
| 		Type:     "m.login.password", | ||||
| 		User:     b.GetString("Login"), | ||||
| 		Password: b.GetString("Password"), | ||||
| 		Type:       "m.login.password", | ||||
| 		User:       b.GetString("Login"), | ||||
| 		Password:   b.GetString("Password"), | ||||
| 		Identifier: matrix.NewUserIdentifier(b.GetString("Login")), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -58,14 +100,18 @@ func (b *Bmatrix) Disconnect() error { | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	resp, err := b.mc.JoinRoom(channel.Name, "", nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b.Lock() | ||||
| 	b.RoomMap[resp.RoomID] = channel.Name | ||||
| 	b.Unlock() | ||||
| 	return err | ||||
| 	return b.retry(func() error { | ||||
| 		resp, err := b.mc.JoinRoom(channel.Name, "", nil) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		b.Lock() | ||||
| 		b.RoomMap[resp.RoomID] = channel.Name | ||||
| 		b.Unlock() | ||||
|  | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) Send(msg config.Message) (string, error) { | ||||
| @@ -74,17 +120,30 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) { | ||||
| 	channel := b.getRoomID(msg.Channel) | ||||
| 	b.Log.Debugf("Channel %s maps to channel id %s", msg.Channel, channel) | ||||
|  | ||||
| 	username := newMatrixUsername(msg.Username) | ||||
|  | ||||
| 	// Make a action /me of the message | ||||
| 	if msg.Event == config.EventUserAction { | ||||
| 		m := matrix.TextMessage{ | ||||
| 			MsgType: "m.emote", | ||||
| 			Body:    msg.Username + msg.Text, | ||||
| 			MsgType:       "m.emote", | ||||
| 			Body:          username.plain + msg.Text, | ||||
| 			FormattedBody: username.formatted + msg.Text, | ||||
| 		} | ||||
| 		resp, err := b.mc.SendMessageEvent(channel, "m.room.message", m) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		return resp.EventID, err | ||||
|  | ||||
| 		msgID := "" | ||||
|  | ||||
| 		err := b.retry(func() error { | ||||
| 			resp, err := b.mc.SendMessageEvent(channel, "m.room.message", m) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			msgID = resp.EventID | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
|  | ||||
| 		return msgID, err | ||||
| 	} | ||||
|  | ||||
| 	// Delete message | ||||
| @@ -92,17 +151,34 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) { | ||||
| 		if msg.ID == "" { | ||||
| 			return "", nil | ||||
| 		} | ||||
| 		resp, err := b.mc.RedactEvent(channel, msg.ID, &matrix.ReqRedact{}) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		return resp.EventID, err | ||||
|  | ||||
| 		msgID := "" | ||||
|  | ||||
| 		err := b.retry(func() error { | ||||
| 			resp, err := b.mc.RedactEvent(channel, msg.ID, &matrix.ReqRedact{}) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			msgID = resp.EventID | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
|  | ||||
| 		return msgID, err | ||||
| 	} | ||||
|  | ||||
| 	// Upload a file if it exists | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||
| 			if _, err := b.mc.SendText(channel, rmsg.Username+rmsg.Text); err != nil { | ||||
| 			rmsg := rmsg | ||||
|  | ||||
| 			err := b.retry(func() error { | ||||
| 				_, err := b.mc.SendText(channel, rmsg.Username+rmsg.Text) | ||||
|  | ||||
| 				return err | ||||
| 			}) | ||||
| 			if err != nil { | ||||
| 				b.Log.Errorf("sendText failed: %s", err) | ||||
| 			} | ||||
| 		} | ||||
| @@ -113,53 +189,105 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) { | ||||
| 	} | ||||
|  | ||||
| 	// Edit message if we have an ID | ||||
| 	// matrix has no editing support | ||||
| 	if msg.ID != "" { | ||||
| 		rmsg := EditedMessage{TextMessage: matrix.TextMessage{ | ||||
| 			Body:    username.plain + msg.Text, | ||||
| 			MsgType: "m.text", | ||||
| 		}} | ||||
| 		if b.GetBool("HTMLDisable") { | ||||
| 			rmsg.TextMessage.FormattedBody = username.formatted + "* " + msg.Text | ||||
| 		} else { | ||||
| 			rmsg.Format = "org.matrix.custom.html" | ||||
| 			rmsg.TextMessage.FormattedBody = username.formatted + "* " + helper.ParseMarkdown(msg.Text) | ||||
| 		} | ||||
| 		rmsg.NewContent = SubTextMessage{ | ||||
| 			Body:    rmsg.TextMessage.Body, | ||||
| 			MsgType: "m.text", | ||||
| 		} | ||||
| 		rmsg.RelatedTo = MessageRelation{ | ||||
| 			EventID: msg.ID, | ||||
| 			Type:    "m.replace", | ||||
| 		} | ||||
|  | ||||
| 	// Use notices to send join/leave events | ||||
| 	if msg.Event == config.EventJoinLeave { | ||||
| 		resp, err := b.mc.SendNotice(channel, msg.Username+msg.Text) | ||||
| 		err := b.retry(func() error { | ||||
| 			_, err := b.mc.SendMessageEvent(channel, "m.room.message", rmsg) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		return msg.ID, nil | ||||
| 	} | ||||
|  | ||||
| 	// Use notices to send join/leave events | ||||
| 	if msg.Event == config.EventJoinLeave { | ||||
| 		m := matrix.TextMessage{ | ||||
| 			MsgType:       "m.notice", | ||||
| 			Body:          username.plain + msg.Text, | ||||
| 			FormattedBody: username.formatted + msg.Text, | ||||
| 		} | ||||
|  | ||||
| 		var ( | ||||
| 			resp *matrix.RespSendEvent | ||||
| 			err  error | ||||
| 		) | ||||
|  | ||||
| 		err = b.retry(func() error { | ||||
| 			resp, err = b.mc.SendMessageEvent(channel, "m.room.message", m) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		return resp.EventID, err | ||||
| 	} | ||||
|  | ||||
| 	if b.GetBool("HTMLDisable") { | ||||
| 		resp, err := b.mc.SendText(channel, msg.Username+msg.Text) | ||||
| 		var ( | ||||
| 			resp *matrix.RespSendEvent | ||||
| 			err  error | ||||
| 		) | ||||
|  | ||||
| 		err = b.retry(func() error { | ||||
| 			resp, err = b.mc.SendText(channel, username.plain+msg.Text) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		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) { | ||||
| 		username = msg.Username | ||||
| 	} | ||||
| 	// Post normal message with HTML support (eg riot.im) | ||||
| 	resp, err := b.mc.SendHTML(channel, msg.Username+msg.Text, username+helper.ParseMarkdown(msg.Text)) | ||||
| 	var ( | ||||
| 		resp *matrix.RespSendEvent | ||||
| 		err  error | ||||
| 	) | ||||
|  | ||||
| 	err = b.retry(func() error { | ||||
| 		resp, err = b.mc.SendFormattedText(channel, username.plain+msg.Text, | ||||
| 			username.formatted+helper.ParseMarkdown(msg.Text)) | ||||
|  | ||||
| 		return err | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return resp.EventID, err | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) getRoomID(channel string) string { | ||||
| 	b.RLock() | ||||
| 	defer b.RUnlock() | ||||
| 	for ID, name := range b.RoomMap { | ||||
| 		if name == channel { | ||||
| 			return ID | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| 	return resp.EventID, err | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) handlematrix() { | ||||
| 	syncer := b.mc.Syncer.(*matrix.DefaultSyncer) | ||||
| 	syncer.OnEventType("m.room.redaction", b.handleEvent) | ||||
| 	syncer.OnEventType("m.room.message", b.handleEvent) | ||||
| 	syncer.OnEventType("m.room.member", b.handleMemberChange) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			if err := b.mc.Sync(); err != nil { | ||||
| @@ -169,6 +297,45 @@ func (b *Bmatrix) handlematrix() { | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) handleEdit(ev *matrix.Event, rmsg config.Message) bool { | ||||
| 	relationInterface, present := ev.Content["m.relates_to"] | ||||
| 	newContentInterface, present2 := ev.Content["m.new_content"] | ||||
| 	if !(present && present2) { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	var relation MessageRelation | ||||
| 	if err := interface2Struct(relationInterface, &relation); err != nil { | ||||
| 		b.Log.Warnf("Couldn't parse 'm.relates_to' object with value %#v", relationInterface) | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	var newContent SubTextMessage | ||||
| 	if err := interface2Struct(newContentInterface, &newContent); err != nil { | ||||
| 		b.Log.Warnf("Couldn't parse 'm.new_content' object with value %#v", newContentInterface) | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if relation.Type != "m.replace" { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	rmsg.ID = relation.EventID | ||||
| 	rmsg.Text = newContent.Body | ||||
| 	b.Remote <- rmsg | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) handleMemberChange(ev *matrix.Event) { | ||||
| 	// Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information | ||||
| 	if ev.Content["membership"] == "join" { | ||||
| 		if dn, ok := ev.Content["displayname"].(string); ok { | ||||
| 			b.cacheDisplayName(ev.Sender, dn) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmatrix) handleEvent(ev *matrix.Event) { | ||||
| 	b.Log.Debugf("== Receiving event: %#v", ev) | ||||
| 	if ev.Sender != b.UserID { | ||||
| @@ -182,7 +349,7 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) { | ||||
|  | ||||
| 		// Create our message | ||||
| 		rmsg := config.Message{ | ||||
| 			Username: ev.Sender[1:], | ||||
| 			Username: b.getDisplayName(ev.Sender), | ||||
| 			Channel:  channel, | ||||
| 			Account:  b.Account, | ||||
| 			UserID:   ev.Sender, | ||||
| @@ -217,6 +384,11 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) { | ||||
| 			rmsg.Event = config.EventUserAction | ||||
| 		} | ||||
|  | ||||
| 		// Is it an edit? | ||||
| 		if b.handleEdit(ev, rmsg) { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// Do we have attachments | ||||
| 		if b.containsAttachment(ev.Content) { | ||||
| 			err := b.handleDownloadFile(&rmsg, ev.Content) | ||||
| @@ -227,6 +399,11 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) { | ||||
|  | ||||
| 		b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account) | ||||
| 		b.Remote <- rmsg | ||||
|  | ||||
| 		// not crucial, so no ratelimit check here | ||||
| 		if err := b.mc.MarkRead(ev.RoomID, ev.ID); err != nil { | ||||
| 			b.Log.Errorf("couldn't mark message as read %s", err.Error()) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -301,27 +478,30 @@ func (b *Bmatrix) handleUploadFiles(msg *config.Message, channel string) (string | ||||
|  | ||||
| // handleUploadFile handles native upload of a file. | ||||
| func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *config.FileInfo) { | ||||
| 	username := newMatrixUsername(msg.Username) | ||||
| 	content := bytes.NewReader(*fi.Data) | ||||
| 	sp := strings.Split(fi.Name, ".") | ||||
| 	mtype := mime.TypeByExtension("." + sp[len(sp)-1]) | ||||
| 	if !(strings.Contains(mtype, "image") || strings.Contains(mtype, "video") || | ||||
| 		strings.Contains(mtype, "application") || strings.Contains(mtype, "audio")) { | ||||
| 		return | ||||
| 	} | ||||
| 	if fi.Comment != "" { | ||||
| 		_, err := b.mc.SendText(channel, msg.Username+fi.Comment) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("file comment failed: %#v", err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// image and video uploads send no username, we have to do this ourself here #715 | ||||
| 		_, err := b.mc.SendText(channel, msg.Username) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("file comment failed: %#v", err) | ||||
| 		} | ||||
| 	// image and video uploads send no username, we have to do this ourself here #715 | ||||
| 	err := b.retry(func() error { | ||||
| 		_, err := b.mc.SendFormattedText(channel, username.plain+fi.Comment, username.formatted+fi.Comment) | ||||
|  | ||||
| 		return err | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("file comment failed: %#v", err) | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("uploading file: %s %s", fi.Name, mtype) | ||||
| 	res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data))) | ||||
|  | ||||
| 	var res *matrix.RespMediaUpload | ||||
|  | ||||
| 	err = b.retry(func() error { | ||||
| 		res, err = b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data))) | ||||
|  | ||||
| 		return err | ||||
| 	}) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("file upload failed: %#v", err) | ||||
| 		return | ||||
| @@ -330,58 +510,60 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf | ||||
| 	switch { | ||||
| 	case strings.Contains(mtype, "video"): | ||||
| 		b.Log.Debugf("sendVideo %s", res.ContentURI) | ||||
| 		_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI) | ||||
| 		err = b.retry(func() error { | ||||
| 			_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("sendVideo failed: %#v", err) | ||||
| 		} | ||||
| 	case strings.Contains(mtype, "image"): | ||||
| 		b.Log.Debugf("sendImage %s", res.ContentURI) | ||||
| 		_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI) | ||||
| 		err = b.retry(func() error { | ||||
| 			_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("sendImage failed: %#v", err) | ||||
| 		} | ||||
| 	case strings.Contains(mtype, "application"): | ||||
| 		b.Log.Debugf("sendFile %s", res.ContentURI) | ||||
| 		_, err = b.mc.SendFile(channel, fi.Name, res.ContentURI, mtype, uint(len(*fi.Data))) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("sendFile failed: %#v", err) | ||||
| 		} | ||||
| 	case strings.Contains(mtype, "audio"): | ||||
| 		b.Log.Debugf("sendAudio %s", res.ContentURI) | ||||
| 		_, err = b.mc.SendAudio(channel, fi.Name, res.ContentURI, mtype, uint(len(*fi.Data))) | ||||
| 		err = b.retry(func() error { | ||||
| 			_, err = b.mc.SendMessageEvent(channel, "m.room.message", matrix.AudioMessage{ | ||||
| 				MsgType: "m.audio", | ||||
| 				Body:    fi.Name, | ||||
| 				URL:     res.ContentURI, | ||||
| 				Info: matrix.AudioInfo{ | ||||
| 					Mimetype: mtype, | ||||
| 					Size:     uint(len(*fi.Data)), | ||||
| 				}, | ||||
| 			}) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("sendAudio failed: %#v", err) | ||||
| 		} | ||||
| 	default: | ||||
| 		b.Log.Debugf("sendFile %s", res.ContentURI) | ||||
| 		err = b.retry(func() error { | ||||
| 			_, err = b.mc.SendMessageEvent(channel, "m.room.message", matrix.FileMessage{ | ||||
| 				MsgType: "m.file", | ||||
| 				Body:    fi.Name, | ||||
| 				URL:     res.ContentURI, | ||||
| 				Info: matrix.FileInfo{ | ||||
| 					Mimetype: mtype, | ||||
| 					Size:     uint(len(*fi.Data)), | ||||
| 				}, | ||||
| 			}) | ||||
|  | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("sendFile failed: %#v", err) | ||||
| 		} | ||||
| 	} | ||||
| 	b.Log.Debugf("result: %#v", res) | ||||
| } | ||||
|  | ||||
| // skipMessages returns true if this message should not be handled | ||||
| func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool { | ||||
| 	// Skip empty messages | ||||
| 	if content["msgtype"] == nil { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// Only allow image,video or file msgtypes | ||||
| 	if !(content["msgtype"].(string) == "m.image" || | ||||
| 		content["msgtype"].(string) == "m.video" || | ||||
| 		content["msgtype"].(string) == "m.file") { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // getAvatarURL returns the avatar URL of the specified sender | ||||
| func (b *Bmatrix) getAvatarURL(sender string) string { | ||||
| 	mxcURL, err := b.mc.GetSenderAvatarURL(sender) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("getAvatarURL failed: %s", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	url := strings.ReplaceAll(mxcURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/") | ||||
| 	if url != "" { | ||||
| 		url += "?width=37&height=37&method=crop" | ||||
| 	} | ||||
| 	return url | ||||
| } | ||||
|   | ||||
							
								
								
									
										28
									
								
								bridge/matrix/matrix_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								bridge/matrix/matrix_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package bmatrix | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestPlainUsername(t *testing.T) { | ||||
| 	uut := newMatrixUsername("MyUser") | ||||
|  | ||||
| 	assert.Equal(t, "MyUser", uut.formatted) | ||||
| 	assert.Equal(t, "MyUser", uut.plain) | ||||
| } | ||||
|  | ||||
| func TestHTMLUsername(t *testing.T) { | ||||
| 	uut := newMatrixUsername("<b>MyUser</b>") | ||||
|  | ||||
| 	assert.Equal(t, "<b>MyUser</b>", uut.formatted) | ||||
| 	assert.Equal(t, "MyUser", uut.plain) | ||||
| } | ||||
|  | ||||
| func TestFancyUsername(t *testing.T) { | ||||
| 	uut := newMatrixUsername("<MyUser>") | ||||
|  | ||||
| 	assert.Equal(t, "<MyUser>", uut.formatted) | ||||
| 	assert.Equal(t, "<MyUser>", uut.plain) | ||||
| } | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/42wim/matterbridge/matterclient" | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| ) | ||||
|  | ||||
| // handleDownloadAvatar downloads the avatar of userid from channel | ||||
| @@ -108,7 +108,7 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) { | ||||
| 			Channel:  message.Channel, | ||||
| 			Text:     message.Text, | ||||
| 			ID:       message.Post.Id, | ||||
| 			ParentID: message.Post.ParentId, | ||||
| 			ParentID: message.Post.RootId, // ParentID is obsolete with mattermost | ||||
| 			Extra:    make(map[string][]interface{}), | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import ( | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/42wim/matterbridge/matterclient" | ||||
| 	"github.com/42wim/matterbridge/matterhook" | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| ) | ||||
|  | ||||
| func (b *Bmattermost) doConnectWebhookBind() error { | ||||
|   | ||||
| @@ -122,11 +122,20 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) { | ||||
| 	} | ||||
|  | ||||
| 	// Handle prefix hint for unthreaded messages. | ||||
| 	if msg.ParentID == "msg-parent-not-found" { | ||||
| 	if msg.ParentNotFound() { | ||||
| 		msg.ParentID = "" | ||||
| 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||
| 	} | ||||
|  | ||||
| 	// we only can reply to the root of the thread, not to a specific ID (like discord for example does) | ||||
| 	if msg.ParentID != "" { | ||||
| 		post, res := b.mc.Client.GetPost(msg.ParentID, "") | ||||
| 		if res.Error != nil { | ||||
| 			b.Log.Errorf("getting post %s failed: %s", msg.ParentID, res.Error.DetailedError) | ||||
| 		} | ||||
| 		msg.ParentID = post.RootId | ||||
| 	} | ||||
|  | ||||
| 	// Upload a file if it exists | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||
|   | ||||
| @@ -86,13 +86,16 @@ func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error { | ||||
|  | ||||
| func (b *Bmsteams) Send(msg config.Message) (string, error) { | ||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | ||||
| 	if msg.ParentID != "" && msg.ParentID != "msg-parent-not-found" { | ||||
| 	if msg.ParentValid() { | ||||
| 		return b.sendReply(msg) | ||||
| 	} | ||||
| 	if msg.ParentID == "msg-parent-not-found" { | ||||
|  | ||||
| 	// Handle prefix hint for unthreaded messages. | ||||
| 	if msg.ParentNotFound() { | ||||
| 		msg.ParentID = "" | ||||
| 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||
| 	} | ||||
|  | ||||
| 	ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().Request() | ||||
| 	text := msg.Username + msg.Text | ||||
| 	content := &msgraph.ItemBody{Content: &text} | ||||
|   | ||||
							
								
								
									
										96
									
								
								bridge/mumble/handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								bridge/mumble/handlers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| package bmumble | ||||
|  | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"layeh.com/gumble/gumble" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| ) | ||||
|  | ||||
| func (b *Bmumble) handleServerConfig(event *gumble.ServerConfigEvent) { | ||||
| 	b.serverConfigUpdate <- *event | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) handleTextMessage(event *gumble.TextMessageEvent) { | ||||
| 	sender := "unknown" | ||||
| 	if event.TextMessage.Sender != nil { | ||||
| 		sender = event.TextMessage.Sender.Name | ||||
| 	} | ||||
| 	// If the text message is received before receiving a ServerSync | ||||
| 	// and UserState, Client.Self or Self.Channel are nil | ||||
| 	if event.Client.Self == nil || event.Client.Self.Channel == nil { | ||||
| 		b.Log.Warn("Connection bootstrap not finished, discarding text message") | ||||
| 		return | ||||
| 	} | ||||
| 	// Convert Mumble HTML messages to markdown | ||||
| 	parts, err := b.convertHTMLtoMarkdown(event.TextMessage.Message) | ||||
| 	if err != nil { | ||||
| 		b.Log.Error(err) | ||||
| 	} | ||||
| 	now := time.Now().UTC() | ||||
| 	for i, part := range parts { | ||||
| 		// Construct matterbridge message and pass on to the gateway | ||||
| 		rmsg := config.Message{ | ||||
| 			Channel:  strconv.FormatUint(uint64(event.Client.Self.Channel.ID), 10), | ||||
| 			Username: sender, | ||||
| 			UserID:   sender + "@" + b.Host, | ||||
| 			Account:  b.Account, | ||||
| 		} | ||||
| 		if part.Image == nil { | ||||
| 			rmsg.Text = part.Text | ||||
| 		} else { | ||||
| 			fname := b.Account + "_" + strconv.FormatInt(now.UnixNano(), 10) + "_" + strconv.Itoa(i) + part.FileExtension | ||||
| 			rmsg.Extra = make(map[string][]interface{}) | ||||
| 			if err = helper.HandleDownloadSize(b.Log, &rmsg, fname, int64(len(part.Image)), b.General); err != nil { | ||||
| 				b.Log.WithError(err).Warn("not including image in message") | ||||
| 				continue | ||||
| 			} | ||||
| 			helper.HandleDownloadData(b.Log, &rmsg, fname, "", "", &part.Image, b.General) | ||||
| 		} | ||||
| 		b.Log.Debugf("Sending message to gateway: %+v", rmsg) | ||||
| 		b.Remote <- rmsg | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) handleConnect(event *gumble.ConnectEvent) { | ||||
| 	// Set the user's "bio"/comment | ||||
| 	if comment := b.GetString("UserComment"); comment != "" && event.Client.Self != nil { | ||||
| 		event.Client.Self.SetComment(comment) | ||||
| 	} | ||||
| 	// No need to talk or listen | ||||
| 	event.Client.Self.SetSelfDeafened(true) | ||||
| 	event.Client.Self.SetSelfMuted(true) | ||||
| 	// if the Channel variable is set, this is a reconnect -> rejoin channel | ||||
| 	if b.Channel != nil { | ||||
| 		if err := b.doJoin(event.Client, *b.Channel); err != nil { | ||||
| 			b.Log.Error(err) | ||||
| 		} | ||||
| 		b.Remote <- config.Message{ | ||||
| 			Username: "system", | ||||
| 			Text:     "rejoin", | ||||
| 			Channel:  "", | ||||
| 			Account:  b.Account, | ||||
| 			Event:    config.EventRejoinChannels, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) handleUserChange(event *gumble.UserChangeEvent) { | ||||
| 	// Only care about changes to self | ||||
| 	if event.User != event.Client.Self { | ||||
| 		return | ||||
| 	} | ||||
| 	// Someone attempted to move the user out of the configured channel; attempt to join back | ||||
| 	if b.Channel != nil { | ||||
| 		if err := b.doJoin(event.Client, *b.Channel); err != nil { | ||||
| 			b.Log.Error(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) handleDisconnect(event *gumble.DisconnectEvent) { | ||||
| 	b.connected <- *event | ||||
| } | ||||
							
								
								
									
										143
									
								
								bridge/mumble/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								bridge/mumble/helpers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| package bmumble | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"mime" | ||||
| 	"net/http" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/mattn/godown" | ||||
| 	"github.com/vincent-petithory/dataurl" | ||||
| ) | ||||
|  | ||||
| type MessagePart struct { | ||||
| 	Text          string | ||||
| 	FileExtension string | ||||
| 	Image         []byte | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) decodeImage(uri string, parts *[]MessagePart) error { | ||||
| 	// Decode the data:image/... URI | ||||
| 	image, err := dataurl.DecodeString(uri) | ||||
| 	if err != nil { | ||||
| 		b.Log.WithError(err).Info("No image extracted") | ||||
| 		return err | ||||
| 	} | ||||
| 	// Determine the file extensions for that image | ||||
| 	ext, err := mime.ExtensionsByType(image.MediaType.ContentType()) | ||||
| 	if err != nil || len(ext) == 0 { | ||||
| 		b.Log.WithError(err).Infof("No file extension registered for MIME type '%s'", image.MediaType.ContentType()) | ||||
| 		return err | ||||
| 	} | ||||
| 	// Add the image to the MessagePart slice | ||||
| 	*parts = append(*parts, MessagePart{"", ext[0], image.Data}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) tokenize(t *string) ([]MessagePart, error) { | ||||
| 	// `^(.*?)` matches everything before the image | ||||
| 	// `!\[[^\]]*\]\(` matches the `]+)` matches the data: URI used by Mumble | ||||
| 	// `\)` matches the closing parenthesis after the URI | ||||
| 	// `(.*)$` matches the remaining text to be examined in the next iteration | ||||
| 	p := regexp.MustCompile(`^(?ms)(.*?)!\[[^\]]*\]\((data:image\/[^)]+)\)(.*)$`) | ||||
| 	remaining := *t | ||||
| 	var parts []MessagePart | ||||
| 	for { | ||||
| 		tokens := p.FindStringSubmatch(remaining) | ||||
| 		if tokens == nil { | ||||
| 			// no match -> remaining string is non-image text | ||||
| 			pre := strings.TrimSpace(remaining) | ||||
| 			if len(pre) > 0 { | ||||
| 				parts = append(parts, MessagePart{pre, "", nil}) | ||||
| 			} | ||||
| 			return parts, nil | ||||
| 		} | ||||
|  | ||||
| 		// tokens[1] is the text before the image | ||||
| 		if len(tokens[1]) > 0 { | ||||
| 			pre := strings.TrimSpace(tokens[1]) | ||||
| 			parts = append(parts, MessagePart{pre, "", nil}) | ||||
| 		} | ||||
| 		// tokens[2] is the image URL | ||||
| 		uri, err := dataurl.UnescapeToString(strings.TrimSpace(strings.ReplaceAll(tokens[2], " ", ""))) | ||||
| 		if err != nil { | ||||
| 			b.Log.WithError(err).Info("URL unescaping failed") | ||||
| 			remaining = strings.TrimSpace(tokens[3]) | ||||
| 			continue | ||||
| 		} | ||||
| 		err = b.decodeImage(uri, &parts) | ||||
| 		if err != nil { | ||||
| 			b.Log.WithError(err).Info("Decoding the image failed") | ||||
| 		} | ||||
| 		// tokens[3] is the text after the image, processed in the next iteration | ||||
| 		remaining = strings.TrimSpace(tokens[3]) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) convertHTMLtoMarkdown(html string) ([]MessagePart, error) { | ||||
| 	var sb strings.Builder | ||||
| 	err := godown.Convert(&sb, strings.NewReader(html), nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	markdown := sb.String() | ||||
| 	b.Log.Debugf("### to markdown: %s", markdown) | ||||
| 	return b.tokenize(&markdown) | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) extractFiles(msg *config.Message) []config.Message { | ||||
| 	var messages []config.Message | ||||
| 	if msg.Extra == nil || len(msg.Extra["file"]) == 0 { | ||||
| 		return messages | ||||
| 	} | ||||
| 	// Create a separate message for each file | ||||
| 	for _, f := range msg.Extra["file"] { | ||||
| 		fi := f.(config.FileInfo) | ||||
| 		imsg := config.Message{ | ||||
| 			Channel:   msg.Channel, | ||||
| 			Username:  msg.Username, | ||||
| 			UserID:    msg.UserID, | ||||
| 			Account:   msg.Account, | ||||
| 			Protocol:  msg.Protocol, | ||||
| 			Timestamp: msg.Timestamp, | ||||
| 			Event:     "mumble_image", | ||||
| 		} | ||||
| 		// If no data is present for the file, send a link instead | ||||
| 		if fi.Data == nil || len(*fi.Data) == 0 { | ||||
| 			if len(fi.URL) > 0 { | ||||
| 				imsg.Text = fmt.Sprintf(`<a href="%s">%s</a>`, fi.URL, fi.URL) | ||||
| 				messages = append(messages, imsg) | ||||
| 			} else { | ||||
| 				b.Log.Infof("Not forwarding file without local data") | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		mimeType := http.DetectContentType(*fi.Data) | ||||
| 		// Mumble only supports images natively, send a link instead | ||||
| 		if !strings.HasPrefix(mimeType, "image/") { | ||||
| 			if len(fi.URL) > 0 { | ||||
| 				imsg.Text = fmt.Sprintf(`<a href="%s">%s</a>`, fi.URL, fi.URL) | ||||
| 				messages = append(messages, imsg) | ||||
| 			} else { | ||||
| 				b.Log.Infof("Not forwarding file of type %s", mimeType) | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		mimeType = strings.TrimSpace(strings.Split(mimeType, ";")[0]) | ||||
| 		// Build data:image/...;base64,... style image URL and embed image directly into the message | ||||
| 		du := dataurl.New(*fi.Data, mimeType) | ||||
| 		dataURL, err := du.MarshalText() | ||||
| 		if err != nil { | ||||
| 			b.Log.WithError(err).Infof("Image Serialization into data URL failed (type: %s, length: %d)", mimeType, len(*fi.Data)) | ||||
| 			continue | ||||
| 		} | ||||
| 		imsg.Text = fmt.Sprintf(`<img src="%s"/>`, dataURL) | ||||
| 		messages = append(messages, imsg) | ||||
| 	} | ||||
| 	// Remove files from original message | ||||
| 	msg.Extra["file"] = nil | ||||
| 	return messages | ||||
| } | ||||
							
								
								
									
										259
									
								
								bridge/mumble/mumble.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								bridge/mumble/mumble.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,259 @@ | ||||
| package bmumble | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"crypto/x509" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"layeh.com/gumble/gumble" | ||||
| 	"layeh.com/gumble/gumbleutil" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	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 | ||||
| 	_ "github.com/paulrosania/go-charset/data" | ||||
| ) | ||||
|  | ||||
| type Bmumble struct { | ||||
| 	client             *gumble.Client | ||||
| 	Nick               string | ||||
| 	Host               string | ||||
| 	Channel            *uint32 | ||||
| 	local              chan config.Message | ||||
| 	running            chan error | ||||
| 	connected          chan gumble.DisconnectEvent | ||||
| 	serverConfigUpdate chan gumble.ServerConfigEvent | ||||
| 	serverConfig       gumble.ServerConfigEvent | ||||
| 	tlsConfig          tls.Config | ||||
|  | ||||
| 	*bridge.Config | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	b := &Bmumble{} | ||||
| 	b.Config = cfg | ||||
| 	b.Nick = b.GetString("Nick") | ||||
| 	b.local = make(chan config.Message) | ||||
| 	b.running = make(chan error) | ||||
| 	b.connected = make(chan gumble.DisconnectEvent) | ||||
| 	b.serverConfigUpdate = make(chan gumble.ServerConfigEvent) | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) Connect() error { | ||||
| 	b.Log.Infof("Connecting %s", b.GetString("Server")) | ||||
| 	host, portstr, err := net.SplitHostPort(b.GetString("Server")) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b.Host = host | ||||
| 	_, err = strconv.Atoi(portstr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err = b.buildTLSConfig(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	go b.doSend() | ||||
| 	go b.connectLoop() | ||||
| 	err = <-b.running | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) Disconnect() error { | ||||
| 	return b.client.Disconnect() | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	cid, err := strconv.ParseUint(channel.Name, 10, 32) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	channelID := uint32(cid) | ||||
| 	if b.Channel != nil && *b.Channel != channelID { | ||||
| 		b.Log.Fatalf("Cannot join channel ID '%d', already joined to channel ID %d", channelID, *b.Channel) | ||||
| 		return errors.New("the Mumble bridge can only join a single channel") | ||||
| 	} | ||||
| 	b.Channel = &channelID | ||||
| 	return b.doJoin(b.client, channelID) | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) Send(msg config.Message) (string, error) { | ||||
| 	// Only process text messages | ||||
| 	b.Log.Debugf("=> Received local message %#v", msg) | ||||
| 	if msg.Event != "" && msg.Event != config.EventUserAction { | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	attachments := b.extractFiles(&msg) | ||||
| 	b.local <- msg | ||||
| 	for _, a := range attachments { | ||||
| 		b.local <- a | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) buildTLSConfig() error { | ||||
| 	b.tlsConfig = tls.Config{} | ||||
| 	// Load TLS client certificate keypair required for registered user authentication | ||||
| 	if cpath := b.GetString("TLSClientCertificate"); cpath != "" { | ||||
| 		if ckey := b.GetString("TLSClientKey"); ckey != "" { | ||||
| 			cert, err := tls.LoadX509KeyPair(cpath, ckey) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			b.tlsConfig.Certificates = []tls.Certificate{cert} | ||||
| 		} | ||||
| 	} | ||||
| 	// Load TLS CA used for server verification.  If not provided, the Go system trust anchor is used | ||||
| 	if capath := b.GetString("TLSCACertificate"); capath != "" { | ||||
| 		ca, err := ioutil.ReadFile(capath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		b.tlsConfig.RootCAs = x509.NewCertPool() | ||||
| 		b.tlsConfig.RootCAs.AppendCertsFromPEM(ca) | ||||
| 	} | ||||
| 	b.tlsConfig.InsecureSkipVerify = b.GetBool("SkipTLSVerify") | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) connectLoop() { | ||||
| 	firstConnect := true | ||||
| 	for { | ||||
| 		err := b.doConnect() | ||||
| 		if firstConnect { | ||||
| 			b.running <- err | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("Connection to server failed: %#v", err) | ||||
| 			if firstConnect { | ||||
| 				break | ||||
| 			} else { | ||||
| 				b.Log.Info("Retrying in 10s") | ||||
| 				time.Sleep(10 * time.Second) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		firstConnect = false | ||||
| 		d := <-b.connected | ||||
| 		switch d.Type { | ||||
| 		case gumble.DisconnectError: | ||||
| 			b.Log.Errorf("Lost connection to the server (%s), attempting reconnect", d.String) | ||||
| 			continue | ||||
| 		case gumble.DisconnectKicked: | ||||
| 			b.Log.Errorf("Kicked from the server (%s), attempting reconnect", d.String) | ||||
| 			continue | ||||
| 		case gumble.DisconnectBanned: | ||||
| 			b.Log.Errorf("Banned from the server (%s), not attempting reconnect", d.String) | ||||
| 			close(b.connected) | ||||
| 			close(b.running) | ||||
| 			return | ||||
| 		case gumble.DisconnectUser: | ||||
| 			b.Log.Infof("Disconnect successful") | ||||
| 			close(b.connected) | ||||
| 			close(b.running) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) doConnect() error { | ||||
| 	// Create new gumble config and attach event handlers | ||||
| 	gumbleConfig := gumble.NewConfig() | ||||
| 	gumbleConfig.Attach(gumbleutil.Listener{ | ||||
| 		ServerConfig: b.handleServerConfig, | ||||
| 		TextMessage:  b.handleTextMessage, | ||||
| 		Connect:      b.handleConnect, | ||||
| 		Disconnect:   b.handleDisconnect, | ||||
| 		UserChange:   b.handleUserChange, | ||||
| 	}) | ||||
| 	gumbleConfig.Username = b.GetString("Nick") | ||||
| 	if password := b.GetString("Password"); password != "" { | ||||
| 		gumbleConfig.Password = password | ||||
| 	} | ||||
|  | ||||
| 	client, err := gumble.DialWithDialer(new(net.Dialer), b.GetString("Server"), gumbleConfig, &b.tlsConfig) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b.client = client | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) doJoin(client *gumble.Client, channelID uint32) error { | ||||
| 	channel, ok := client.Channels[channelID] | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("no channel with ID %d", channelID) | ||||
| 	} | ||||
| 	client.Self.Move(channel) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) doSend() { | ||||
| 	// Message sending loop that makes sure server-side | ||||
| 	// restrictions and client-side message traits don't conflict | ||||
| 	// with each other. | ||||
| 	for { | ||||
| 		select { | ||||
| 		case serverConfig := <-b.serverConfigUpdate: | ||||
| 			b.Log.Debugf("Received server config update: AllowHTML=%#v, MaximumMessageLength=%#v", serverConfig.AllowHTML, serverConfig.MaximumMessageLength) | ||||
| 			b.serverConfig = serverConfig | ||||
| 		case msg := <-b.local: | ||||
| 			b.processMessage(&msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bmumble) processMessage(msg *config.Message) { | ||||
| 	b.Log.Debugf("Processing message %s", msg.Text) | ||||
|  | ||||
| 	allowHTML := true | ||||
| 	if b.serverConfig.AllowHTML != nil { | ||||
| 		allowHTML = *b.serverConfig.AllowHTML | ||||
| 	} | ||||
|  | ||||
| 	// If this is a specially generated image message, send it unmodified | ||||
| 	if msg.Event == "mumble_image" { | ||||
| 		if allowHTML { | ||||
| 			b.client.Self.Channel.Send(msg.Username+msg.Text, false) | ||||
| 		} else { | ||||
| 			b.Log.Info("Can't send image, server does not allow HTML messages") | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Don't process empty messages | ||||
| 	if len(msg.Text) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	// If HTML is allowed, convert markdown into HTML, otherwise strip markdown | ||||
| 	if allowHTML { | ||||
| 		msg.Text = helper.ParseMarkdown(msg.Text) | ||||
| 	} else { | ||||
| 		msg.Text = stripmd.Strip(msg.Text) | ||||
| 	} | ||||
|  | ||||
| 	// If there is a maximum message length, split and truncate the lines | ||||
| 	var msgLines []string | ||||
| 	if maxLength := b.serverConfig.MaximumMessageLength; maxLength != nil { | ||||
| 		msgLines = helper.GetSubLines(msg.Text, *maxLength-len(msg.Username)) | ||||
| 	} else { | ||||
| 		msgLines = helper.GetSubLines(msg.Text, 0) | ||||
| 	} | ||||
| 	// Send the individual lindes | ||||
| 	for i := range msgLines { | ||||
| 		b.client.Self.Channel.Send(msg.Username+msgLines[i], false) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										210
									
								
								bridge/nctalk/nctalk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								bridge/nctalk/nctalk.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| package nctalk | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
|  | ||||
| 	"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") | ||||
| 	tconfig := &user.TalkUserConfig{ | ||||
| 		TLSConfig: &tls.Config{ | ||||
| 			InsecureSkipVerify: b.GetBool("SkipTLSVerify"), //nolint:gosec | ||||
| 		}, | ||||
| 	} | ||||
| 	var err error | ||||
| 	b.user, err = user.NewUser(b.GetString("Server"), b.GetString("Login"), b.GetString("Password"), tconfig) | ||||
| 	if err != nil { | ||||
| 		b.Log.Error("Config could not be used") | ||||
| 		return err | ||||
| 	} | ||||
| 	_, 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 { | ||||
| 	tr, err := room.NewTalkRoom(b.user, channel.Name) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	newRoom := Broom{ | ||||
| 		room: tr, | ||||
| 	} | ||||
| 	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) | ||||
|  | ||||
| 	// Config | ||||
| 	guestSuffix := " (Guest)" | ||||
| 	if b.IsKeySet("GuestSuffix") { | ||||
| 		guestSuffix = b.GetString("GuestSuffix") | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		for msg := range c { | ||||
| 			msg := msg | ||||
|  | ||||
| 			if msg.Error != nil { | ||||
| 				b.Log.Errorf("Fatal message poll error: %s\n", msg.Error) | ||||
|  | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			// 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:     formatRichObjectString(msg.Message, msg.MessageParameters), | ||||
| 				Channel:  newRoom.room.Token, | ||||
| 				Username: DisplayName(msg, guestSuffix), | ||||
| 				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) | ||||
| 			} | ||||
|  | ||||
| 			// Handle Files | ||||
| 			err = b.handleFiles(&remoteMessage, &msg) | ||||
| 			if err != nil { | ||||
| 				b.Log.Errorf("Error handling file: %#v", msg) | ||||
|  | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			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 | ||||
| } | ||||
|  | ||||
| func (b *Btalk) handleFiles(mmsg *config.Message, message *ocs.TalkRoomMessageData) error { | ||||
| 	for _, parameter := range message.MessageParameters { | ||||
| 		if parameter.Type == ocs.ROSTypeFile { | ||||
| 			// Get the file | ||||
| 			file, err := b.user.DownloadFile(parameter.Path) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if mmsg.Extra == nil { | ||||
| 				mmsg.Extra = make(map[string][]interface{}) | ||||
| 			} | ||||
|  | ||||
| 			mmsg.Extra["file"] = append(mmsg.Extra["file"], config.FileInfo{ | ||||
| 				Name:   parameter.Name, | ||||
| 				Data:   file, | ||||
| 				Size:   int64(len(*file)), | ||||
| 				Avatar: false, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Spec: https://github.com/nextcloud/server/issues/1706#issue-182308785 | ||||
| func formatRichObjectString(message string, parameters map[string]ocs.RichObjectString) string { | ||||
| 	for id, parameter := range parameters { | ||||
| 		text := parameter.Name | ||||
|  | ||||
| 		switch parameter.Type { | ||||
| 		case ocs.ROSTypeUser, ocs.ROSTypeGroup: | ||||
| 			text = "@" + text | ||||
| 		case ocs.ROSTypeFile: | ||||
| 			if parameter.Link != "" { | ||||
| 				text = parameter.Name | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		message = strings.ReplaceAll(message, "{"+id+"}", text) | ||||
| 	} | ||||
|  | ||||
| 	return message | ||||
| } | ||||
|  | ||||
| func DisplayName(msg ocs.TalkRoomMessageData, suffix string) string { | ||||
| 	if msg.ActorType == ocs.ActorGuest { | ||||
| 		if msg.ActorDisplayName == "" { | ||||
| 			return "Guest" | ||||
| 		} | ||||
|  | ||||
| 		return msg.ActorDisplayName + suffix | ||||
| 	} | ||||
|  | ||||
| 	return msg.ActorDisplayName | ||||
| } | ||||
| @@ -1,7 +1,10 @@ | ||||
| package brocketchat | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
| 	"github.com/matterbridge/Rocket.Chat.Go.SDK/models" | ||||
| ) | ||||
|  | ||||
| @@ -58,6 +61,7 @@ func (b *Brocketchat) handleStatusEvent(ev models.Message, rmsg *config.Message) | ||||
|  | ||||
| func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | ||||
| 	for message := range b.messageChan { | ||||
| 		message := message | ||||
| 		// skip messages with same ID, apparently messages get duplicated for an unknown reason | ||||
| 		if _, ok := b.cache.Get(message.ID); ok { | ||||
| 			continue | ||||
| @@ -76,8 +80,11 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | ||||
| 			Account:  b.Account, | ||||
| 			UserID:   message.User.ID, | ||||
| 			ID:       message.ID, | ||||
| 			Extra:    make(map[string][]interface{}), | ||||
| 		} | ||||
|  | ||||
| 		b.handleAttachments(&message, 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) { | ||||
| @@ -86,6 +93,38 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Brocketchat) handleAttachments(message *models.Message, rmsg *config.Message) { | ||||
| 	if rmsg.Text == "" { | ||||
| 		for _, attachment := range message.Attachments { | ||||
| 			if attachment.Title != "" { | ||||
| 				rmsg.Text = attachment.Title + "\n" | ||||
| 			} | ||||
| 			if attachment.Title != "" && attachment.Text != "" { | ||||
| 				rmsg.Text += "\n" | ||||
| 			} | ||||
| 			if attachment.Text != "" { | ||||
| 				rmsg.Text += attachment.Text | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i := range message.Attachments { | ||||
| 		if err := b.handleDownloadFile(rmsg, &message.Attachments[i]); err != nil { | ||||
| 			b.Log.Errorf("Could not download incoming file: %#v", err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Brocketchat) handleDownloadFile(rmsg *config.Message, file *models.Attachment) error { | ||||
| 	downloadURL := b.GetString("server") + file.TitleLink | ||||
| 	data, err := helper.DownloadFileAuthRocket(downloadURL, b.user.Token, b.user.ID) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("download %s failed %#v", downloadURL, err) | ||||
| 	} | ||||
| 	helper.HandleDownloadData(b.Log, rmsg, file.Title, rmsg.Text, downloadURL, data, b.General) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Brocketchat) handleUploadFile(msg *config.Message) error { | ||||
| 	for _, f := range msg.Extra["file"] { | ||||
| 		fi := f.(config.FileInfo) | ||||
|   | ||||
| @@ -299,7 +299,7 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) { | ||||
| 	} | ||||
|  | ||||
| 	// Handle prefix hint for unthreaded messages. | ||||
| 	if msg.ParentID == "msg-parent-not-found" { | ||||
| 	if msg.ParentNotFound() { | ||||
| 		msg.ParentID = "" | ||||
| 		msg.Text = fmt.Sprintf("[thread]: %s", msg.Text) | ||||
| 	} | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package btelegram | ||||
|  | ||||
| import ( | ||||
| 	"html" | ||||
| 	"regexp" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode/utf16" | ||||
| @@ -181,13 +181,15 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { | ||||
| // sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful. | ||||
| // logs an error message if it fails | ||||
| func (b *Btelegram) handleDownloadAvatar(userid int, channel string) { | ||||
| 	rmsg := config.Message{Username: "system", | ||||
| 		Text:    "avatar", | ||||
| 		Channel: channel, | ||||
| 		Account: b.Account, | ||||
| 		UserID:  strconv.Itoa(userid), | ||||
| 		Event:   config.EventAvatarDownload, | ||||
| 		Extra:   make(map[string][]interface{})} | ||||
| 	rmsg := config.Message{ | ||||
| 		Username: "system", | ||||
| 		Text:     "avatar", | ||||
| 		Channel:  channel, | ||||
| 		Account:  b.Account, | ||||
| 		UserID:   strconv.Itoa(userid), | ||||
| 		Event:    config.EventAvatarDownload, | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := b.avatarMap[strconv.Itoa(userid)]; !ok { | ||||
| 		photos, err := b.c.GetUserProfilePhotos(tgbotapi.UserProfilePhotosConfig{UserID: userid, Limit: 1}) | ||||
| @@ -217,6 +219,46 @@ func (b *Btelegram) handleDownloadAvatar(userid int, channel string) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Btelegram) maybeConvertTgs(name *string, data *[]byte) { | ||||
| 	var format string | ||||
| 	switch b.GetString("MediaConvertTgs") { | ||||
| 	case FormatWebp: | ||||
| 		b.Log.Debugf("Tgs to WebP conversion enabled, converting %v", name) | ||||
| 		format = FormatWebp | ||||
| 	case FormatPng: | ||||
| 		// The WebP to PNG converter can't handle animated webp files yet, | ||||
| 		// and I'm not going to write a path for x/image/webp. | ||||
| 		// The error message would be: | ||||
| 		//     conversion failed: webp: non-Alpha VP8X is not implemented | ||||
| 		// So instead, we tell lottie to directly go to PNG. | ||||
| 		b.Log.Debugf("Tgs to PNG conversion enabled, converting %v", name) | ||||
| 		format = FormatPng | ||||
| 	default: | ||||
| 		// Otherwise, no conversion was requested. Trying to run the usual webp | ||||
| 		// converter would fail, because '.tgs.webp' is actually a gzipped JSON | ||||
| 		// file, and has nothing to do with WebP. | ||||
| 		return | ||||
| 	} | ||||
| 	err := helper.ConvertTgsToX(data, format, b.Log) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("conversion failed: %v", err) | ||||
| 	} else { | ||||
| 		*name = strings.Replace(*name, "tgs.webp", format, 1) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Btelegram) maybeConvertWebp(name *string, data *[]byte) { | ||||
| 	if b.GetBool("MediaConvertWebPToPNG") { | ||||
| 		b.Log.Debugf("WebP to PNG conversion enabled, converting %v", name) | ||||
| 		err := helper.ConvertWebPToPNG(data) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("conversion failed: %v", err) | ||||
| 		} else { | ||||
| 			*name = strings.Replace(*name, ".webp", ".png", 1) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // handleDownloadFile handles file download | ||||
| func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error { | ||||
| 	size := 0 | ||||
| @@ -264,15 +306,18 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if strings.HasSuffix(name, ".webp") && b.GetBool("MediaConvertWebPToPNG") { | ||||
| 		b.Log.Debugf("WebP to PNG conversion enabled, converting %s", name) | ||||
| 		err := helper.ConvertWebPToPNG(data) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("conversion failed: %s", err) | ||||
| 		} else { | ||||
| 			name = strings.Replace(name, ".webp", ".png", 1) | ||||
| 		} | ||||
|  | ||||
| 	if strings.HasSuffix(name, ".tgs.webp") { | ||||
| 		b.maybeConvertTgs(&name, data) | ||||
| 	} else if strings.HasSuffix(name, ".webp") { | ||||
| 		b.maybeConvertWebp(&name, data) | ||||
| 	} | ||||
|  | ||||
| 	// rename .oga to .ogg  https://github.com/42wim/matterbridge/issues/906#issuecomment-741793512 | ||||
| 	if strings.HasSuffix(name, ".oga") && message.Audio != nil { | ||||
| 		name = strings.Replace(name, ".oga", ".ogg", 1) | ||||
| 	} | ||||
|  | ||||
| 	helper.HandleDownloadData(b.Log, rmsg, name, message.Caption, "", data, b.General) | ||||
| 	return nil | ||||
| } | ||||
| @@ -322,6 +367,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") | ||||
| @@ -343,21 +391,32 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64) string { | ||||
| 			Name:  fi.Name, | ||||
| 			Bytes: *fi.Data, | ||||
| 		} | ||||
| 		re := regexp.MustCompile(".(jpg|png)$") | ||||
| 		if re.MatchString(fi.Name) { | ||||
| 			c = tgbotapi.NewPhotoUpload(chatid, file) | ||||
| 		} else { | ||||
| 			c = tgbotapi.NewDocumentUpload(chatid, file) | ||||
| 		switch filepath.Ext(fi.Name) { | ||||
| 		case ".jpg", ".jpe", ".png": | ||||
| 			pc := tgbotapi.NewPhotoUpload(chatid, file) | ||||
| 			pc.Caption, pc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) | ||||
| 			c = pc | ||||
| 		case ".mp4", ".m4v": | ||||
| 			vc := tgbotapi.NewVideoUpload(chatid, file) | ||||
| 			vc.Caption, vc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) | ||||
| 			c = vc | ||||
| 		case ".mp3", ".oga": | ||||
| 			ac := tgbotapi.NewAudioUpload(chatid, file) | ||||
| 			ac.Caption, ac.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) | ||||
| 			c = ac | ||||
| 		case ".ogg": | ||||
| 			voc := tgbotapi.NewVoiceUpload(chatid, file) | ||||
| 			voc.Caption, voc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) | ||||
| 			c = voc | ||||
| 		default: | ||||
| 			dc := tgbotapi.NewDocumentUpload(chatid, file) | ||||
| 			dc.Caption, dc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment) | ||||
| 			c = dc | ||||
| 		} | ||||
| 		_, err := b.c.Send(c) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("file upload failed: %#v", err) | ||||
| 		} | ||||
| 		if fi.Comment != "" { | ||||
| 			if _, err := b.sendMessage(chatid, msg.Username, fi.Comment); err != nil { | ||||
| 				b.Log.Errorf("posting file comment %s failed: %s", fi.Comment, err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| @@ -367,7 +426,7 @@ func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string | ||||
| 	if format == "" { | ||||
| 		format = "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})" | ||||
| 	} | ||||
| 	quoteMessagelength := len(quoteMessage) | ||||
| 	quoteMessagelength := len([]rune(quoteMessage)) | ||||
| 	if b.GetInt("QuoteLengthLimit") != 0 && quoteMessagelength >= b.GetInt("QuoteLengthLimit") { | ||||
| 		runes := []rune(quoteMessage) | ||||
| 		quoteMessage = string(runes[0:b.GetInt("QuoteLengthLimit")]) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package btelegram | ||||
|  | ||||
| import ( | ||||
| 	"html" | ||||
| 	"log" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| @@ -15,6 +16,9 @@ const ( | ||||
| 	unknownUser = "unknown" | ||||
| 	HTMLFormat  = "HTML" | ||||
| 	HTMLNick    = "htmlnick" | ||||
| 	MarkdownV2  = "MarkdownV2" | ||||
| 	FormatPng   = "png" | ||||
| 	FormatWebp  = "webp" | ||||
| ) | ||||
|  | ||||
| type Btelegram struct { | ||||
| @@ -24,6 +28,16 @@ type Btelegram struct { | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	tgsConvertFormat := cfg.GetString("MediaConvertTgs") | ||||
| 	if tgsConvertFormat != "" { | ||||
| 		err := helper.CanConvertTgsToX() | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but lottie does not appear to work:\n%#v", tgsConvertFormat, err) | ||||
| 		} | ||||
| 		if tgsConvertFormat != FormatPng && tgsConvertFormat != FormatWebp { | ||||
| 			log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but only '%s' and '%s' are supported.", FormatPng, FormatWebp, tgsConvertFormat) | ||||
| 		} | ||||
| 	} | ||||
| 	return &Btelegram{Config: cfg, avatarMap: make(map[string]string)} | ||||
| } | ||||
|  | ||||
| @@ -55,6 +69,28 @@ func (b *Btelegram) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func TGGetParseMode(b *Btelegram, username string, text string) (textout string, parsemode string) { | ||||
| 	textout = username + text | ||||
| 	if b.GetString("MessageFormat") == HTMLFormat { | ||||
| 		b.Log.Debug("Using mode HTML") | ||||
| 		parsemode = tgbotapi.ModeHTML | ||||
| 	} | ||||
| 	if b.GetString("MessageFormat") == "Markdown" { | ||||
| 		b.Log.Debug("Using mode markdown") | ||||
| 		parsemode = tgbotapi.ModeMarkdown | ||||
| 	} | ||||
| 	if b.GetString("MessageFormat") == MarkdownV2 { | ||||
| 		b.Log.Debug("Using mode MarkdownV2") | ||||
| 		parsemode = MarkdownV2 | ||||
| 	} | ||||
| 	if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick { | ||||
| 		b.Log.Debug("Using mode HTML - nick only") | ||||
| 		textout = username + html.EscapeString(text) | ||||
| 		parsemode = tgbotapi.ModeHTML | ||||
| 	} | ||||
| 	return textout, parsemode | ||||
| } | ||||
|  | ||||
| func (b *Btelegram) Send(msg config.Message) (string, error) { | ||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | ||||
|  | ||||
| @@ -117,20 +153,7 @@ func (b *Btelegram) getFileDirectURL(id string) string { | ||||
|  | ||||
| func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, error) { | ||||
| 	m := tgbotapi.NewMessage(chatid, "") | ||||
| 	m.Text = username + text | ||||
| 	if b.GetString("MessageFormat") == HTMLFormat { | ||||
| 		b.Log.Debug("Using mode HTML") | ||||
| 		m.ParseMode = tgbotapi.ModeHTML | ||||
| 	} | ||||
| 	if b.GetString("MessageFormat") == "Markdown" { | ||||
| 		b.Log.Debug("Using mode markdown") | ||||
| 		m.ParseMode = tgbotapi.ModeMarkdown | ||||
| 	} | ||||
| 	if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick { | ||||
| 		b.Log.Debug("Using mode HTML - nick only") | ||||
| 		m.Text = username + html.EscapeString(text) | ||||
| 		m.ParseMode = tgbotapi.ModeHTML | ||||
| 	} | ||||
| 	m.Text, m.ParseMode = TGGetParseMode(b, username, text) | ||||
|  | ||||
| 	m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview") | ||||
|  | ||||
|   | ||||
							
								
								
									
										327
									
								
								bridge/vk/vk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								bridge/vk/vk.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| package bvk | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/42wim/matterbridge/bridge" | ||||
| 	"github.com/42wim/matterbridge/bridge/config" | ||||
| 	"github.com/42wim/matterbridge/bridge/helper" | ||||
|  | ||||
| 	"github.com/SevereCloud/vksdk/v2/api" | ||||
| 	"github.com/SevereCloud/vksdk/v2/events" | ||||
| 	longpoll "github.com/SevereCloud/vksdk/v2/longpoll-bot" | ||||
| 	"github.com/SevereCloud/vksdk/v2/object" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	audioMessage = "audio_message" | ||||
| 	document     = "doc" | ||||
| 	photo        = "photo" | ||||
| 	video        = "video" | ||||
| 	graffiti     = "graffiti" | ||||
| 	sticker      = "sticker" | ||||
| 	wall         = "wall" | ||||
| ) | ||||
|  | ||||
| type user struct { | ||||
| 	lastname, firstname, avatar string | ||||
| } | ||||
|  | ||||
| type Bvk struct { | ||||
| 	c            *api.VK | ||||
| 	usernamesMap map[int]user // cache of user names and avatar URLs | ||||
| 	*bridge.Config | ||||
| } | ||||
|  | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	return &Bvk{usernamesMap: make(map[int]user), Config: cfg} | ||||
| } | ||||
|  | ||||
| func (b *Bvk) Connect() error { | ||||
| 	b.Log.Info("Connecting") | ||||
| 	b.c = api.NewVK(b.GetString("Token")) | ||||
| 	lp, err := longpoll.NewLongPoll(b.c, b.GetInt("GroupID")) | ||||
| 	if err != nil { | ||||
| 		b.Log.Debugf("%#v", err) | ||||
|  | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	lp.MessageNew(func(ctx context.Context, obj events.MessageNewObject) { | ||||
| 		b.handleMessage(obj.Message, false) | ||||
| 	}) | ||||
|  | ||||
| 	b.Log.Info("Connection succeeded") | ||||
|  | ||||
| 	go func() { | ||||
| 		err := lp.Run() | ||||
| 		if err != nil { | ||||
| 			b.Log.Fatal("Enable longpoll in group management") | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bvk) Disconnect() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bvk) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bvk) Send(msg config.Message) (string, error) { | ||||
| 	b.Log.Debugf("=> Receiving %#v", msg) | ||||
|  | ||||
| 	peerID, err := strconv.Atoi(msg.Channel) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	params := api.Params{} | ||||
|  | ||||
| 	text := msg.Username + msg.Text | ||||
|  | ||||
| 	if msg.Extra != nil { | ||||
| 		if len(msg.Extra["file"]) > 0 { | ||||
| 			// generate attachments string | ||||
| 			attachment, urls := b.uploadFiles(msg.Extra, peerID) | ||||
| 			params["attachment"] = attachment | ||||
| 			text += urls | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	params["message"] = text | ||||
|  | ||||
| 	if msg.ID == "" { | ||||
| 		// New message | ||||
| 		params["random_id"] = time.Now().Unix() | ||||
| 		params["peer_ids"] = msg.Channel | ||||
|  | ||||
| 		res, e := b.c.MessagesSendPeerIDs(params) | ||||
| 		if e != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		return strconv.Itoa(res[0].ConversationMessageID), nil | ||||
| 	} | ||||
| 	// Edit message | ||||
| 	messageID, err := strconv.ParseInt(msg.ID, 10, 64) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	params["peer_id"] = peerID | ||||
| 	params["conversation_message_id"] = messageID | ||||
|  | ||||
| 	_, err = b.c.MessagesEdit(params) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	return msg.ID, nil | ||||
| } | ||||
|  | ||||
| func (b *Bvk) getUser(id int) user { | ||||
| 	u, found := b.usernamesMap[id] | ||||
| 	if !found { | ||||
| 		b.Log.Debug("Fetching username for ", id) | ||||
|  | ||||
| 		if id >= 0 { | ||||
| 			result, _ := b.c.UsersGet(api.Params{ | ||||
| 				"user_ids": id, | ||||
| 				"fields":   "photo_200", | ||||
| 			}) | ||||
|  | ||||
| 			resUser := result[0] | ||||
| 			u = user{lastname: resUser.LastName, firstname: resUser.FirstName, avatar: resUser.Photo200} | ||||
| 			b.usernamesMap[id] = u | ||||
| 		} else { | ||||
| 			result, _ := b.c.GroupsGetByID(api.Params{ | ||||
| 				"group_id": id * -1, | ||||
| 			}) | ||||
|  | ||||
| 			resGroup := result[0] | ||||
| 			u = user{lastname: resGroup.Name, avatar: resGroup.Photo200} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return u | ||||
| } | ||||
|  | ||||
| func (b *Bvk) handleMessage(msg object.MessagesMessage, isFwd bool) { | ||||
| 	b.Log.Debug("ChatID: ", msg.PeerID) | ||||
| 	// fetch user info | ||||
| 	u := b.getUser(msg.FromID) | ||||
|  | ||||
| 	rmsg := config.Message{ | ||||
| 		Text:     msg.Text, | ||||
| 		Username: u.firstname + " " + u.lastname, | ||||
| 		Avatar:   u.avatar, | ||||
| 		Channel:  strconv.Itoa(msg.PeerID), | ||||
| 		Account:  b.Account, | ||||
| 		UserID:   strconv.Itoa(msg.FromID), | ||||
| 		ID:       strconv.Itoa(msg.ConversationMessageID), | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 	} | ||||
|  | ||||
| 	if msg.ReplyMessage != nil { | ||||
| 		ur := b.getUser(msg.ReplyMessage.FromID) | ||||
| 		rmsg.Text = "Re: " + ur.firstname + " " + ur.lastname + "\n" + rmsg.Text | ||||
| 	} | ||||
|  | ||||
| 	if isFwd { | ||||
| 		rmsg.Username = "Fwd: " + rmsg.Username | ||||
| 	} | ||||
|  | ||||
| 	if len(msg.Attachments) > 0 { | ||||
| 		urls, text := b.getFiles(msg.Attachments) | ||||
|  | ||||
| 		if text != "" { | ||||
| 			rmsg.Text += "\n" + text | ||||
| 		} | ||||
|  | ||||
| 		// download | ||||
| 		b.downloadFiles(&rmsg, urls) | ||||
| 	} | ||||
|  | ||||
| 	if len(msg.FwdMessages) > 0 { | ||||
| 		rmsg.Text += strconv.Itoa(len(msg.FwdMessages)) + " forwarded messages" | ||||
| 	} | ||||
|  | ||||
| 	b.Remote <- rmsg | ||||
|  | ||||
| 	if len(msg.FwdMessages) > 0 { | ||||
| 		// recursive processing of forwarded messages | ||||
| 		for _, m := range msg.FwdMessages { | ||||
| 			m.PeerID = msg.PeerID | ||||
| 			b.handleMessage(m, true) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bvk) uploadFiles(extra map[string][]interface{}, peerID int) (string, string) { | ||||
| 	var attachments []string | ||||
| 	text := "" | ||||
|  | ||||
| 	for _, f := range extra["file"] { | ||||
| 		fi := f.(config.FileInfo) | ||||
|  | ||||
| 		if fi.Comment != "" { | ||||
| 			text += fi.Comment + "\n" | ||||
| 		} | ||||
| 		a, err := b.uploadFile(fi, peerID) | ||||
| 		if err != nil { | ||||
| 			b.Log.Error("File upload error ", fi.Name) | ||||
| 		} | ||||
|  | ||||
| 		attachments = append(attachments, a) | ||||
| 	} | ||||
|  | ||||
| 	return strings.Join(attachments, ","), text | ||||
| } | ||||
|  | ||||
| func (b *Bvk) uploadFile(file config.FileInfo, peerID int) (string, error) { | ||||
| 	r := bytes.NewReader(*file.Data) | ||||
|  | ||||
| 	photoRE := regexp.MustCompile(".(jpg|jpe|png)$") | ||||
| 	if photoRE.MatchString(file.Name) { | ||||
| 		p, err := b.c.UploadMessagesPhoto(peerID, r) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		return photo + strconv.Itoa(p[0].OwnerID) + "_" + strconv.Itoa(p[0].ID), nil | ||||
| 	} | ||||
|  | ||||
| 	var doctype string | ||||
| 	if strings.Contains(file.Name, ".ogg") { | ||||
| 		doctype = audioMessage | ||||
| 	} else { | ||||
| 		doctype = document | ||||
| 	} | ||||
|  | ||||
| 	doc, err := b.c.UploadMessagesDoc(peerID, doctype, file.Name, "", r) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	switch doc.Type { | ||||
| 	case audioMessage: | ||||
| 		return document + strconv.Itoa(doc.AudioMessage.OwnerID) + "_" + strconv.Itoa(doc.AudioMessage.ID), nil | ||||
| 	case document: | ||||
| 		return document + strconv.Itoa(doc.Doc.OwnerID) + "_" + strconv.Itoa(doc.Doc.ID), nil | ||||
| 	} | ||||
|  | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| func (b *Bvk) getFiles(attachments []object.MessagesMessageAttachment) ([]string, string) { | ||||
| 	var urls []string | ||||
| 	var text []string | ||||
|  | ||||
| 	for _, a := range attachments { | ||||
| 		switch a.Type { | ||||
| 		case photo: | ||||
| 			var resolution float64 = 0 | ||||
| 			url := a.Photo.Sizes[0].URL | ||||
| 			for _, size := range a.Photo.Sizes { | ||||
| 				r := size.Height * size.Width | ||||
| 				if resolution < r { | ||||
| 					resolution = r | ||||
| 					url = size.URL | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			urls = append(urls, url) | ||||
|  | ||||
| 		case document: | ||||
| 			urls = append(urls, a.Doc.URL) | ||||
|  | ||||
| 		case graffiti: | ||||
| 			urls = append(urls, a.Graffiti.URL) | ||||
|  | ||||
| 		case audioMessage: | ||||
| 			urls = append(urls, a.AudioMessage.DocsDocPreviewAudioMessage.LinkOgg) | ||||
|  | ||||
| 		case sticker: | ||||
| 			var resolution float64 = 0 | ||||
| 			url := a.Sticker.Images[0].URL | ||||
| 			for _, size := range a.Sticker.Images { | ||||
| 				r := size.Height * size.Width | ||||
| 				if resolution < r { | ||||
| 					resolution = r | ||||
| 					url = size.URL | ||||
| 				} | ||||
| 			} | ||||
| 			urls = append(urls, url+".png") | ||||
| 		case video: | ||||
| 			text = append(text, "https://vk.com/video"+strconv.Itoa(a.Video.OwnerID)+"_"+strconv.Itoa(a.Video.ID)) | ||||
|  | ||||
| 		case wall: | ||||
| 			text = append(text, "https://vk.com/wall"+strconv.Itoa(a.Wall.FromID)+"_"+strconv.Itoa(a.Wall.ID)) | ||||
|  | ||||
| 		default: | ||||
| 			text = append(text, "This attachment is not supported ("+a.Type+")") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return urls, strings.Join(text, "\n") | ||||
| } | ||||
|  | ||||
| func (b *Bvk) downloadFiles(rmsg *config.Message, urls []string) { | ||||
| 	for _, url := range urls { | ||||
| 		data, err := helper.DownloadFile(url) | ||||
| 		if err == nil { | ||||
| 			urlPart := strings.Split(url, "/") | ||||
| 			name := strings.Split(urlPart[len(urlPart)-1], "?")[0] | ||||
| 			helper.HandleDownloadData(b.Log, rmsg, name, "", url, data, b.General) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -24,7 +24,8 @@ Check: | ||||
| func (b *Bwhatsapp) HandleError(err error) { | ||||
| 	// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843 | ||||
| 	// 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") { | ||||
| 	if strings.Contains(err.Error(), "error processing data: received invalid data") || | ||||
| 		strings.Contains(err.Error(), "invalid string with tag 174") { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @@ -47,16 +48,22 @@ func (b *Bwhatsapp) reconnect(err error) { | ||||
| 		Max:    5 * time.Minute, | ||||
| 		Jitter: true, | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		d := bf.Duration() | ||||
|  | ||||
| 		b.Log.Errorf("Connection failed, underlying error: %v", err) | ||||
| 		b.Log.Infof("Waiting %s...", d) | ||||
|  | ||||
| 		time.Sleep(d) | ||||
|  | ||||
| 		b.Log.Info("Reconnecting...") | ||||
|  | ||||
| 		err := b.conn.Restore() | ||||
| 		if err == nil { | ||||
| 			bf.Reset() | ||||
| 			b.startedAt = uint64(time.Now().Unix()) | ||||
|  | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| @@ -64,7 +71,7 @@ func (b *Bwhatsapp) reconnect(err error) { | ||||
|  | ||||
| // HandleTextMessage sent from WhatsApp, relay it to the brige | ||||
| func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | ||||
| 	if message.Info.FromMe { // || !strings.Contains(strings.ToLower(message.Text), "@echo") { | ||||
| 	if message.Info.FromMe { | ||||
| 		return | ||||
| 	} | ||||
| 	// whatsapp sends last messages to show context , cut them | ||||
| @@ -72,13 +79,13 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones | ||||
| 	groupJID := message.Info.RemoteJid | ||||
|  | ||||
| 	senderJID := message.Info.SenderJid | ||||
|  | ||||
| 	if len(senderJID) == 0 { | ||||
| 		// TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved | ||||
| 		senderJID = *message.Info.Source.Participant | ||||
| 		if message.Info.Source != nil && message.Info.Source.Participant != nil { | ||||
| 			senderJID = *message.Info.Source.Participant | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// translate sender's JID to the nicest username we can get | ||||
| @@ -99,108 +106,209 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) { | ||||
| 			if mention == "" { | ||||
| 				mention = "someone" | ||||
| 			} | ||||
|  | ||||
| 			message.Text = strings.Replace(message.Text, "@"+numberAndSuffix[0], "@"+mention, 1) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||
| 	rmsg := config.Message{ | ||||
| 		UserID:    senderJID, | ||||
| 		Username:  senderName, | ||||
| 		Text:      message.Text, | ||||
| 		Timestamp: messageTime, | ||||
| 		Channel:   groupJID, | ||||
| 		Account:   b.Account, | ||||
| 		Protocol:  b.Protocol, | ||||
| 		Extra:     make(map[string][]interface{}), | ||||
| 		UserID:   senderJID, | ||||
| 		Username: senderName, | ||||
| 		Text:     message.Text, | ||||
| 		Channel:  groupJID, | ||||
| 		Account:  b.Account, | ||||
| 		Protocol: b.Protocol, | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 		//	ParentID: TODO, // TODO handle thread replies  // map from Info.QuotedMessageID string | ||||
| 		//	Event     string    `json:"event"` | ||||
| 		//	Gateway   string  // will be added during message processing | ||||
| 		ID: message.Info.Id} | ||||
| 		ID: message.Info.Id, | ||||
| 	} | ||||
|  | ||||
| 	if avatarURL, exists := b.userAvatars[senderJID]; exists { | ||||
| 		rmsg.Avatar = avatarURL | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
|  | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|  | ||||
| // HandleImageMessage sent from WhatsApp, relay it to the brige | ||||
| func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) { | ||||
| 	if message.Info.FromMe { // || !strings.Contains(strings.ToLower(message.Text), "@echo") { | ||||
| 	if message.Info.FromMe || message.Info.Timestamp < b.startedAt { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// whatsapp sends last messages to show context , cut them | ||||
| 	if message.Info.Timestamp < b.startedAt { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones | ||||
| 	groupJID := message.Info.RemoteJid | ||||
|  | ||||
| 	senderJID := message.Info.SenderJid | ||||
| 	// if len(senderJid) == 0 { | ||||
| 	//   // TODO workaround till https://github.com/Rhymen/go-whatsapp/issues/86 resolved | ||||
| 	//   senderJid = *message.Info.Source.Participant | ||||
| 	// } | ||||
| 	if len(message.Info.SenderJid) == 0 && message.Info.Source != nil && message.Info.Source.Participant != nil { | ||||
| 		senderJID = *message.Info.Source.Participant | ||||
| 	} | ||||
|  | ||||
| 	// translate sender's Jid to the nicest username we can get | ||||
| 	senderName := b.getSenderName(senderJID) | ||||
| 	senderName := b.getSenderName(message.Info.SenderJid) | ||||
| 	if senderName == "" { | ||||
| 		senderName = "Someone" // don't expose telephone number | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||
| 	rmsg := config.Message{ | ||||
| 		UserID:    senderJID, | ||||
| 		Username:  senderName, | ||||
| 		Timestamp: messageTime, | ||||
| 		Channel:   groupJID, | ||||
| 		Account:   b.Account, | ||||
| 		Protocol:  b.Protocol, | ||||
| 		Extra:     make(map[string][]interface{}), | ||||
| 		//  ParentID: TODO,      // TODO handle thread replies  // map from Info.QuotedMessageID string | ||||
| 		//  Event     string    `json:"event"` | ||||
| 		//  Gateway   string     // will be added during message processing | ||||
| 		ID: message.Info.Id} | ||||
| 		UserID:   senderJID, | ||||
| 		Username: senderName, | ||||
| 		Channel:  message.Info.RemoteJid, | ||||
| 		Account:  b.Account, | ||||
| 		Protocol: b.Protocol, | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 		ID:       message.Info.Id, | ||||
| 	} | ||||
|  | ||||
| 	if avatarURL, exists := b.userAvatars[senderJID]; exists { | ||||
| 		rmsg.Avatar = avatarURL | ||||
| 	} | ||||
|  | ||||
| 	// Download and unencrypt content | ||||
| 	data, err := message.Download() | ||||
| 	fileExt, err := mime.ExtensionsByType(message.Type) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("%v", err) | ||||
| 		b.Log.Errorf("Mimetype detection error: %s", err) | ||||
|  | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Get file extension by mimetype | ||||
| 	// rename .jfif to .jpg https://github.com/42wim/matterbridge/issues/1292 | ||||
| 	if fileExt[0] == ".jfif" { | ||||
| 		fileExt[0] = ".jpg" | ||||
| 	} | ||||
|  | ||||
| 	filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0]) | ||||
|  | ||||
| 	b.Log.Debugf("Trying to download %s with type %s", filename, message.Type) | ||||
|  | ||||
| 	data, err := message.Download() | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Download image failed: %s", err) | ||||
|  | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Move file to bridge storage | ||||
| 	helper.HandleDownloadData(b.Log, &rmsg, filename, message.Caption, "", &data, b.General) | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
|  | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|  | ||||
| // HandleVideoMessage downloads video messages | ||||
| func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) { | ||||
| 	if message.Info.FromMe || message.Info.Timestamp < b.startedAt { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	senderJID := message.Info.SenderJid | ||||
| 	if len(message.Info.SenderJid) == 0 && message.Info.Source != nil && message.Info.Source.Participant != nil { | ||||
| 		senderJID = *message.Info.Source.Participant | ||||
| 	} | ||||
|  | ||||
| 	senderName := b.getSenderName(message.Info.SenderJid) | ||||
| 	if senderName == "" { | ||||
| 		senderName = "Someone" // don't expose telephone number | ||||
| 	} | ||||
|  | ||||
| 	rmsg := config.Message{ | ||||
| 		UserID:   senderJID, | ||||
| 		Username: senderName, | ||||
| 		Channel:  message.Info.RemoteJid, | ||||
| 		Account:  b.Account, | ||||
| 		Protocol: b.Protocol, | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 		ID:       message.Info.Id, | ||||
| 	} | ||||
|  | ||||
| 	if avatarURL, exists := b.userAvatars[senderJID]; exists { | ||||
| 		rmsg.Avatar = avatarURL | ||||
| 	} | ||||
|  | ||||
| 	fileExt, err := mime.ExtensionsByType(message.Type) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("%v", err) | ||||
| 		b.Log.Errorf("Mimetype detection error: %s", err) | ||||
|  | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0]) | ||||
|  | ||||
| 	b.Log.Debugf("<= Image downloaded and unencrypted") | ||||
| 	b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, message.Length, message.Type) | ||||
|  | ||||
| 	data, err := message.Download() | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Download video failed: %s", err) | ||||
|  | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Move file to bridge storage | ||||
| 	helper.HandleDownloadData(b.Log, &rmsg, filename, message.Caption, "", &data, b.General) | ||||
|  | ||||
| 	b.Log.Debugf("<= Image Message is %#v", rmsg) | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
|  | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|  | ||||
| //func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) { | ||||
| //	fmt.Println(message) // TODO implement | ||||
| //} | ||||
| // | ||||
| //func (b *Bwhatsapp) HandleJsonMessage(message string) { | ||||
| //	fmt.Println(message) // TODO implement | ||||
| //} | ||||
| // TODO HandleRawMessage | ||||
| // TODO HandleAudioMessage | ||||
| // HandleAudioMessage downloads audio messages | ||||
| func (b *Bwhatsapp) HandleAudioMessage(message whatsapp.AudioMessage) { | ||||
| 	if message.Info.FromMe || message.Info.Timestamp < b.startedAt { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	senderJID := message.Info.SenderJid | ||||
| 	if len(message.Info.SenderJid) == 0 && message.Info.Source != nil && message.Info.Source.Participant != nil { | ||||
| 		senderJID = *message.Info.Source.Participant | ||||
| 	} | ||||
|  | ||||
| 	senderName := b.getSenderName(message.Info.SenderJid) | ||||
| 	if senderName == "" { | ||||
| 		senderName = "Someone" // don't expose telephone number | ||||
| 	} | ||||
|  | ||||
| 	rmsg := config.Message{ | ||||
| 		UserID:   senderJID, | ||||
| 		Username: senderName, | ||||
| 		Channel:  message.Info.RemoteJid, | ||||
| 		Account:  b.Account, | ||||
| 		Protocol: b.Protocol, | ||||
| 		Extra:    make(map[string][]interface{}), | ||||
| 		ID:       message.Info.Id, | ||||
| 	} | ||||
|  | ||||
| 	if avatarURL, exists := b.userAvatars[senderJID]; exists { | ||||
| 		rmsg.Avatar = avatarURL | ||||
| 	} | ||||
|  | ||||
| 	fileExt, err := mime.ExtensionsByType(message.Type) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Mimetype detection error: %s", err) | ||||
|  | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if len(fileExt) == 0 { | ||||
| 		fileExt = append(fileExt, ".ogg") | ||||
| 	} | ||||
|  | ||||
| 	filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0]) | ||||
|  | ||||
| 	b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, message.Length, message.Type) | ||||
|  | ||||
| 	data, err := message.Download() | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Download audio failed: %s", err) | ||||
|  | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Move file to bridge storage | ||||
| 	helper.HandleDownloadData(b.Log, &rmsg, filename, "audio message", "", &data, b.General) | ||||
|  | ||||
| 	b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account) | ||||
| 	b.Log.Debugf("<= Message is %#v", rmsg) | ||||
|  | ||||
| 	b.Remote <- rmsg | ||||
| } | ||||
|   | ||||
| @@ -6,22 +6,24 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go" | ||||
| 	"github.com/Rhymen/go-whatsapp" | ||||
| ) | ||||
|  | ||||
| type ProfilePicInfo struct { | ||||
| 	URL string `json:"eurl"` | ||||
| 	Tag string `json:"tag"` | ||||
|  | ||||
| 	Status int16 `json:"status"` | ||||
| 	URL    string `json:"eurl"` | ||||
| 	Tag    string `json:"tag"` | ||||
| 	Status int16  `json:"status"` | ||||
| } | ||||
|  | ||||
| func qrFromTerminal(invert bool) chan string { | ||||
| 	qr := make(chan string) | ||||
|  | ||||
| 	go func() { | ||||
| 		terminal := qrcodeTerminal.New() | ||||
|  | ||||
| 		if invert { | ||||
| 			terminal = qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightWhite, qrcodeTerminal.ConsoleColors.BrightBlack, qrcodeTerminal.QRCodeRecoveryLevels.Medium) | ||||
| 		} | ||||
| @@ -44,13 +46,12 @@ func (b *Bwhatsapp) readSession() (whatsapp.Session, error) { | ||||
| 	if err != nil { | ||||
| 		return session, err | ||||
| 	} | ||||
|  | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	decoder := gob.NewDecoder(file) | ||||
| 	err = decoder.Decode(&session) | ||||
| 	if err != nil { | ||||
| 		return session, err | ||||
| 	} | ||||
| 	return session, nil | ||||
|  | ||||
| 	return session, decoder.Decode(&session) | ||||
| } | ||||
|  | ||||
| func (b *Bwhatsapp) writeSession(session whatsapp.Session) error { | ||||
| @@ -65,11 +66,31 @@ func (b *Bwhatsapp) writeSession(session whatsapp.Session) error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	encoder := gob.NewEncoder(file) | ||||
| 	err = encoder.Encode(session) | ||||
|  | ||||
| 	return err | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	encoder := gob.NewEncoder(file) | ||||
|  | ||||
| 	return encoder.Encode(session) | ||||
| } | ||||
|  | ||||
| func (b *Bwhatsapp) restoreSession() (*whatsapp.Session, error) { | ||||
| 	session, err := b.readSession() | ||||
| 	if err != nil { | ||||
| 		b.Log.Warn(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugln("Restoring WhatsApp session..") | ||||
|  | ||||
| 	session, err = b.conn.RestoreWithSession(session) | ||||
| 	if err != nil { | ||||
| 		// restore session connection timed out (I couldn't get over it without logging in again) | ||||
| 		return nil, errors.New("failed to restore session: " + err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	b.Log.Debugln("Session restored successfully!") | ||||
|  | ||||
| 	return &session, nil | ||||
| } | ||||
|  | ||||
| func (b *Bwhatsapp) getSenderName(senderJid string) string { | ||||
| @@ -80,8 +101,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 "" | ||||
| } | ||||
|  | ||||
| @@ -89,6 +135,7 @@ func (b *Bwhatsapp) getSenderNotify(senderJid string) string { | ||||
| 	if sender, exists := b.users[senderJid]; exists { | ||||
| 		return sender.Notify | ||||
| 	} | ||||
|  | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| @@ -97,11 +144,20 @@ func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) { | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to get avatar: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	content := <-data | ||||
| 	info := &ProfilePicInfo{} | ||||
|  | ||||
| 	err = json.Unmarshal([]byte(content), info) | ||||
| 	if err != nil { | ||||
| 		return info, fmt.Errorf("failed to unmarshal avatar info: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return info, nil | ||||
| } | ||||
|  | ||||
| func isGroupJid(identifier string) bool { | ||||
| 	return strings.HasSuffix(identifier, "@g.us") || | ||||
| 		strings.HasSuffix(identifier, "@temp") || | ||||
| 		strings.HasSuffix(identifier, "@broadcast") | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,6 @@ const ( | ||||
| type Bwhatsapp struct { | ||||
| 	*bridge.Config | ||||
|  | ||||
| 	// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21 | ||||
| 	session   *whatsapp.Session | ||||
| 	conn      *whatsapp.Conn | ||||
| 	startedAt uint64 | ||||
| @@ -40,6 +39,7 @@ type Bwhatsapp struct { | ||||
| // New Create a new WhatsApp bridge. This will be called for each [whatsapp.<server>] entry you have in the config file | ||||
| func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 	number := cfg.GetString(cfgNumber) | ||||
|  | ||||
| 	if number == "" { | ||||
| 		cfg.Log.Fatalf("Missing configuration for WhatsApp bridge: Number") | ||||
| 	} | ||||
| @@ -50,24 +50,19 @@ func New(cfg *bridge.Config) bridge.Bridger { | ||||
| 		users:       make(map[string]whatsapp.Contact), | ||||
| 		userAvatars: make(map[string]string), | ||||
| 	} | ||||
|  | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| // Connect to WhatsApp. Required implementation of the Bridger interface | ||||
| // https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16 | ||||
| func (b *Bwhatsapp) Connect() error { | ||||
| 	b.RLock() // TODO do we need locking for Whatsapp? | ||||
| 	defer b.RUnlock() | ||||
|  | ||||
| 	number := b.GetString(cfgNumber) | ||||
| 	if number == "" { | ||||
| 		return errors.New("WhatsApp's telephone Number need to be configured") | ||||
| 		return errors.New("whatsapp's telephone number need to be configured") | ||||
| 	} | ||||
|  | ||||
| 	// https://github.com/Rhymen/go-whatsapp#creating-a-connection | ||||
| 	b.Log.Debugln("Connecting to WhatsApp..") | ||||
| 	conn, err := whatsapp.NewConn(20 * time.Second) | ||||
| 	conn.SetClientVersion(0, 4, 2080) | ||||
| 	if err != nil { | ||||
| 		return errors.New("failed to connect to WhatsApp: " + err.Error()) | ||||
| 	} | ||||
| @@ -78,35 +73,18 @@ func (b *Bwhatsapp) Connect() error { | ||||
| 	b.Log.Debugln("WhatsApp connection successful") | ||||
|  | ||||
| 	// load existing session in order to keep it between restarts | ||||
| 	if b.session == nil { | ||||
| 		var session whatsapp.Session | ||||
| 		session, err = b.readSession() | ||||
|  | ||||
| 		if err == nil { | ||||
| 			b.Log.Debugln("Restoring WhatsApp session..") | ||||
|  | ||||
| 			// https://github.com/Rhymen/go-whatsapp#restore | ||||
| 			session, err = b.conn.RestoreWithSession(session) | ||||
| 			if err != nil { | ||||
| 				// TODO return or continue to normal login? | ||||
| 				// restore session connection timed out (I couldn't get over it without logging in again) | ||||
| 				return errors.New("failed to restore session: " + err.Error()) | ||||
| 			} | ||||
|  | ||||
| 			b.session = &session | ||||
| 			b.Log.Debugln("Session restored successfully!") | ||||
| 		} else { | ||||
| 			b.Log.Warn(err.Error()) | ||||
| 		} | ||||
| 	b.session, err = b.restoreSession() | ||||
| 	if err != nil { | ||||
| 		b.Log.Warn(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	// login to a new session | ||||
| 	if b.session == nil { | ||||
| 		err = b.Login() | ||||
| 		if err != nil { | ||||
| 		if err = b.Login(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b.startedAt = uint64(time.Now().Unix()) | ||||
|  | ||||
| 	_, err = b.conn.Contacts() | ||||
| @@ -114,6 +92,13 @@ func (b *Bwhatsapp) Connect() error { | ||||
| 		return fmt.Errorf("error on update of contacts: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// see https://github.com/Rhymen/go-whatsapp/issues/137#issuecomment-480316013 | ||||
| 	for len(b.conn.Store.Contacts) == 0 { | ||||
| 		b.conn.Contacts() // nolint:errcheck | ||||
|  | ||||
| 		<-time.After(1 * time.Second) | ||||
| 	} | ||||
|  | ||||
| 	// map all the users | ||||
| 	for id, contact := range b.conn.Store.Contacts { | ||||
| 		if !isGroupJid(id) && id != "status@broadcast" { | ||||
| @@ -130,12 +115,13 @@ func (b *Bwhatsapp) Connect() error { | ||||
| 			info, err := b.GetProfilePicThumb(jid) | ||||
| 			if err != nil { | ||||
| 				b.Log.Warnf("Could not get profile photo of %s: %v", jid, err) | ||||
|  | ||||
| 			} else { | ||||
| 				// TODO any race conditions here? | ||||
| 				b.Lock() | ||||
| 				b.userAvatars[jid] = info.URL | ||||
| 				b.Unlock() | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		b.Log.Debug("Finished getting avatars..") | ||||
| 	}() | ||||
|  | ||||
| @@ -152,8 +138,10 @@ func (b *Bwhatsapp) Login() error { | ||||
| 	session, err := b.conn.Login(qrChan) | ||||
| 	if err != nil { | ||||
| 		b.Log.Warnln("Failed to log in:", err) | ||||
|  | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	b.session = &session | ||||
|  | ||||
| 	b.Log.Infof("Logged into session: %#v", session) | ||||
| @@ -164,74 +152,62 @@ func (b *Bwhatsapp) Login() error { | ||||
| 		fmt.Fprintf(os.Stderr, "error saving session: %v\n", err) | ||||
| 	} | ||||
|  | ||||
| 	// TODO change connection strings to configured ones longClientName:"github.com/rhymen/go-whatsapp", shortClientName:"go-whatsapp"}" prefix=whatsapp | ||||
| 	// TODO get also a nice logo | ||||
|  | ||||
| 	// TODO notification about unplugged and dead battery | ||||
| 	// conn.Info: Wid, Pushname, Connected, Battery, Plugged | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Disconnect is called while reconnecting to the bridge | ||||
| // TODO 42wim Documentation would be helpful on when reconnects happen and what should be done in this function | ||||
| // Required implementation of the Bridger interface | ||||
| // https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16 | ||||
| func (b *Bwhatsapp) Disconnect() error { | ||||
| 	// We could Logout, but that would close the session completely and would require a new QR code scan | ||||
| 	// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L377-L381 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func isGroupJid(identifier string) bool { | ||||
| 	return strings.HasSuffix(identifier, "@g.us") || strings.HasSuffix(identifier, "@temp") | ||||
| } | ||||
|  | ||||
| // JoinChannel Join a WhatsApp group specified in gateway config as channel='number-id@g.us' or channel='Channel name' | ||||
| // Required implementation of the Bridger interface | ||||
| // https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16 | ||||
| func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error { | ||||
| 	byJid := isGroupJid(channel.Name) | ||||
|  | ||||
| 	// see https://github.com/Rhymen/go-whatsapp/issues/137#issuecomment-480316013 | ||||
| 	for len(b.conn.Store.Contacts) == 0 { | ||||
| 		b.conn.Contacts() // nolint:errcheck | ||||
| 		<-time.After(1 * time.Second) | ||||
| 	} | ||||
|  | ||||
| 	// verify if we are member of the given group | ||||
| 	if byJid { | ||||
| 		// channel.Name specifies static group jID, not the name | ||||
| 		if _, exists := b.conn.Store.Contacts[channel.Name]; !exists { | ||||
| 			return fmt.Errorf("account doesn't belong to group with jid %s", channel.Name) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// channel.Name specifies group name that might change, warn about it | ||||
| 		var jids []string | ||||
| 		for id, contact := range b.conn.Store.Contacts { | ||||
| 			if isGroupJid(id) && contact.Name == channel.Name { | ||||
| 				jids = append(jids, id) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		switch len(jids) { | ||||
| 		case 0: | ||||
| 			// didn't match any group - print out possibilites | ||||
| 			// TODO sort | ||||
| 			// copy b; | ||||
| 			//sort.Slice(people, func(i, j int) bool { | ||||
| 			//	return people[i].Age > people[j].Age | ||||
| 			//}) | ||||
| 			for id, contact := range b.conn.Store.Contacts { | ||||
| 				if isGroupJid(id) { | ||||
| 					b.Log.Infof("%s %s", contact.Jid, contact.Name) | ||||
| 				} | ||||
| 			} | ||||
| 			return fmt.Errorf("please specify group's JID from the list above instead of the name '%s'", channel.Name) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 		case 1: | ||||
| 			return fmt.Errorf("group name might change. Please configure gateway with channel=\"%v\" instead of channel=\"%v\"", jids[0], channel.Name) | ||||
|  | ||||
| 		default: | ||||
| 			return fmt.Errorf("there is more than one group with name '%s'. Please specify one of JIDs as channel name: %v", channel.Name, jids) | ||||
| 	// channel.Name specifies group name that might change, warn about it | ||||
| 	var jids []string | ||||
| 	for id, contact := range b.conn.Store.Contacts { | ||||
| 		if isGroupJid(id) && contact.Name == channel.Name { | ||||
| 			jids = append(jids, id) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| 	switch len(jids) { | ||||
| 	case 0: | ||||
| 		// didn't match any group - print out possibilites | ||||
| 		for id, contact := range b.conn.Store.Contacts { | ||||
| 			if isGroupJid(id) { | ||||
| 				b.Log.Infof("%s %s", contact.Jid, contact.Name) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return fmt.Errorf("please specify group's JID from the list above instead of the name '%s'", channel.Name) | ||||
| 	case 1: | ||||
| 		return fmt.Errorf("group name might change. Please configure gateway with channel=\"%v\" instead of channel=\"%v\"", jids[0], channel.Name) | ||||
| 	default: | ||||
| 		return fmt.Errorf("there is more than one group with name '%s'. Please specify one of JIDs as channel name: %v", channel.Name, jids) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Post a document message from the bridge to WhatsApp | ||||
| @@ -305,14 +281,12 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) { | ||||
| 		if msg.ID == "" { | ||||
| 			// No message ID in case action is executed on a message sent before the bridge was started | ||||
| 			// and then the bridge cache doesn't have this message ID mapped | ||||
|  | ||||
| 			// TODO 42wim Doesn't the app get clogged with a ton of IDs after some time of running? | ||||
| 			// WhatsApp allows to set any ID so in that case we could use external IDs and don't do mapping | ||||
| 			// but external IDs are not set | ||||
| 			return "", nil | ||||
| 		} | ||||
| 		// TODO delete message on WhatsApp https://github.com/Rhymen/go-whatsapp/issues/100 | ||||
| 		return "", nil | ||||
|  | ||||
| 		_, err := b.conn.RevokeMessage(msg.Channel, msg.ID, true) | ||||
|  | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	// Edit message | ||||
| @@ -320,7 +294,6 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) { | ||||
| 		b.Log.Debugf("updating message with id %s", msg.ID) | ||||
|  | ||||
| 		msg.Text += " (edited)" | ||||
| 		// TODO handle edit as a message reply with updated text | ||||
| 	} | ||||
|  | ||||
| 	// Handle Upload a file | ||||
| @@ -350,16 +323,7 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) { | ||||
|  | ||||
| 	b.Log.Debugf("=> Sending %#v", msg) | ||||
|  | ||||
| 	// create message ID | ||||
| 	// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented | ||||
| 	idBytes := make([]byte, 10) | ||||
| 	if _, err := rand.Read(idBytes); err != nil { | ||||
| 		b.Log.Warn(err.Error()) | ||||
| 	} | ||||
| 	message.Info.Id = strings.ToUpper(hex.EncodeToString(idBytes)) | ||||
| 	_, err := b.conn.Send(message) | ||||
|  | ||||
| 	return message.Info.Id, err | ||||
| 	return b.conn.Send(message) | ||||
| } | ||||
|  | ||||
| // TODO do we want that? to allow login with QR code from a bridged channel? https://github.com/tulir/mautrix-whatsapp/blob/513eb18e2d59bada0dd515ee1abaaf38a3bfe3d5/commands.go#L76 | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
| package bxmpp | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| @@ -86,14 +90,21 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) { | ||||
| 	} | ||||
|  | ||||
| 	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support). | ||||
| 	var err error | ||||
| 	if msg.Extra != nil { | ||||
| 		for _, rmsg := range helper.HandleExtra(&msg, b.General) { | ||||
| 			b.Log.Debugf("=> Sending attachement message %#v", rmsg) | ||||
| 			if _, err := b.xc.Send(xmpp.Chat{ | ||||
| 				Type:   "groupchat", | ||||
| 				Remote: rmsg.Channel + "@" + b.GetString("Muc"), | ||||
| 				Text:   rmsg.Username + rmsg.Text, | ||||
| 			}); err != nil { | ||||
| 			if b.GetString("WebhookURL") != "" { | ||||
| 				err = b.postSlackCompatibleWebhook(msg) | ||||
| 			} else { | ||||
| 				_, err = b.xc.Send(xmpp.Chat{ | ||||
| 					Type:   "groupchat", | ||||
| 					Remote: rmsg.Channel + "@" + b.GetString("Muc"), | ||||
| 					Text:   rmsg.Username + rmsg.Text, | ||||
| 				}) | ||||
| 			} | ||||
|  | ||||
| 			if err != nil { | ||||
| 				b.Log.WithError(err).Error("Unable to send message with share URL.") | ||||
| 			} | ||||
| 		} | ||||
| @@ -102,13 +113,24 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if b.GetString("WebhookURL") != "" { | ||||
| 		b.Log.Debugf("Sending message using Webhook") | ||||
| 		err := b.postSlackCompatibleWebhook(msg) | ||||
| 		if err != nil { | ||||
| 			b.Log.Errorf("Failed to send message using webhook: %s", err) | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	// Post normal message. | ||||
| 	var msgReplaceID string | ||||
| 	msgID := xid.New().String() | ||||
| 	if msg.ID != "" { | ||||
| 		msgID = msg.ID | ||||
| 		msgReplaceID = msg.ID | ||||
| 	} | ||||
| 	// Post normal message. | ||||
| 	b.Log.Debugf("=> Sending message %#v", msg) | ||||
| 	if _, err := b.xc.Send(xmpp.Chat{ | ||||
| 		Type:      "groupchat", | ||||
| @@ -122,6 +144,30 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) { | ||||
| 	return msgID, nil | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) postSlackCompatibleWebhook(msg config.Message) error { | ||||
| 	type XMPPWebhook struct { | ||||
| 		Username string `json:"username"` | ||||
| 		Text     string `json:"text"` | ||||
| 	} | ||||
| 	webhookBody, err := json.Marshal(XMPPWebhook{ | ||||
| 		Username: msg.Username, | ||||
| 		Text:     msg.Text, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Failed to marshal webhook: %s", err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	resp, err := http.Post(b.GetString("WebhookURL")+"/"+url.QueryEscape(msg.Channel), "application/json", bytes.NewReader(webhookBody)) | ||||
| 	if err != nil { | ||||
| 		b.Log.Errorf("Failed to POST webhook: %s", err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	resp.Body.Close() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bxmpp) createXMPP() error { | ||||
| 	if !strings.Contains(b.GetString("Jid"), "@") { | ||||
| 		return fmt.Errorf("the Jid %s doesn't contain an @", b.GetString("Jid")) | ||||
| @@ -138,14 +184,14 @@ func (b *Bxmpp) createXMPP() error { | ||||
| 		User:                         b.GetString("Jid"), | ||||
| 		Password:                     b.GetString("Password"), | ||||
| 		NoTLS:                        true, | ||||
| 		StartTLS:                     true, | ||||
| 		StartTLS:                     !b.GetBool("NoTLS"), | ||||
| 		TLSConfig:                    tc, | ||||
| 		Debug:                        b.GetBool("debug"), | ||||
| 		Session:                      true, | ||||
| 		Status:                       "", | ||||
| 		StatusMessage:                "", | ||||
| 		Resource:                     "", | ||||
| 		InsecureAllowUnencryptedAuth: false, | ||||
| 		InsecureAllowUnencryptedAuth: b.GetBool("NoTLS"), | ||||
| 	} | ||||
| 	var err error | ||||
| 	b.xc, err = options.NewClient() | ||||
| @@ -378,6 +424,11 @@ func (b *Bxmpp) skipMessage(message xmpp.Chat) bool { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	// Ignore messages posted by our webhook | ||||
| 	if b.GetString("WebhookURL") != "" && strings.Contains(message.ID, "webhookbot") { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	// skip delayed messages | ||||
| 	return !message.Stamp.IsZero() && time.Since(message.Stamp).Minutes() > 5 | ||||
| } | ||||
|   | ||||
| @@ -135,19 +135,25 @@ func (b *Bzulip) handleQueue() error { | ||||
| 			if m.SenderEmail == b.GetString("login") { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			avatarURL := m.AvatarURL | ||||
| 			if !strings.HasPrefix(avatarURL, "http") { | ||||
| 				avatarURL = b.GetString("server") + avatarURL | ||||
| 			} | ||||
|  | ||||
| 			rmsg := config.Message{ | ||||
| 				Username: m.SenderFullName, | ||||
| 				Text:     m.Content, | ||||
| 				Channel:  b.getChannel(m.StreamID) + "/topic:" + m.Subject, | ||||
| 				Account:  b.Account, | ||||
| 				UserID:   strconv.Itoa(m.SenderID), | ||||
| 				Avatar:   m.AvatarURL, | ||||
| 				Avatar:   avatarURL, | ||||
| 			} | ||||
| 			b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) | ||||
| 			b.Log.Debugf("<= Message is %#v", rmsg) | ||||
| 			b.Remote <- rmsg | ||||
| 			b.q.LastEventID = m.ID | ||||
| 		} | ||||
|  | ||||
| 		time.Sleep(time.Second * 3) | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										1420
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										1420
									
								
								changelog.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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/bmumble.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bmumble.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nomumble | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bmumble "github.com/42wim/matterbridge/bridge/mumble" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["mumble"] = bmumble.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bnctalk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bnctalk.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !nonctalk | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	btalk "github.com/42wim/matterbridge/bridge/nctalk" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["nctalk"] = btalk.New | ||||
| } | ||||
							
								
								
									
										11
									
								
								gateway/bridgemap/bvk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								gateway/bridgemap/bvk.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // +build !novk | ||||
|  | ||||
| package bridgemap | ||||
|  | ||||
| import ( | ||||
| 	bvk "github.com/42wim/matterbridge/bridge/vk" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	FullMap["vk"] = bvk.New | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| package gateway | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| @@ -13,7 +14,7 @@ import ( | ||||
| 	"github.com/d5/tengo/v2" | ||||
| 	"github.com/d5/tengo/v2/stdlib" | ||||
| 	lru "github.com/hashicorp/golang-lru" | ||||
| 	"github.com/matterbridge/emoji" | ||||
| 	"github.com/kyokomi/emoji/v2" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| @@ -126,7 +127,7 @@ func (gw *Gateway) AddConfig(cfg *config.Gateway) error { | ||||
| 		gw.logger.Errorf("mapChannels() failed: %s", err) | ||||
| 	} | ||||
| 	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) { | ||||
| 		br := br //scopelint | ||||
| 		br := br // scopelint | ||||
| 		err := gw.AddBridge(&br) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| @@ -336,20 +337,21 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri | ||||
| 			} | ||||
| 			i++ | ||||
| 		} | ||||
| 		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1) | ||||
| 		nick = strings.ReplaceAll(nick, "{NOPINGNICK}", msg.Username[:i]+"\u200b"+msg.Username[i:]) | ||||
| 	} | ||||
|  | ||||
| 	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1) | ||||
| 	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1) | ||||
| 	nick = strings.Replace(nick, "{GATEWAY}", gw.Name, -1) | ||||
| 	nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1) | ||||
| 	nick = strings.Replace(nick, "{NICK}", msg.Username, -1) | ||||
| 	nick = strings.Replace(nick, "{CHANNEL}", msg.Channel, -1) | ||||
| 	nick = strings.ReplaceAll(nick, "{BRIDGE}", br.Name) | ||||
| 	nick = strings.ReplaceAll(nick, "{PROTOCOL}", br.Protocol) | ||||
| 	nick = strings.ReplaceAll(nick, "{GATEWAY}", gw.Name) | ||||
| 	nick = strings.ReplaceAll(nick, "{LABEL}", br.GetString("Label")) | ||||
| 	nick = strings.ReplaceAll(nick, "{NICK}", msg.Username) | ||||
| 	nick = strings.ReplaceAll(nick, "{USERID}", msg.UserID) | ||||
| 	nick = strings.ReplaceAll(nick, "{CHANNEL}", msg.Channel) | ||||
| 	tengoNick, err := gw.modifyUsernameTengo(msg, br) | ||||
| 	if err != nil { | ||||
| 		gw.logger.Errorf("modifyUsernameTengo error: %s", err) | ||||
| 	} | ||||
| 	nick = strings.Replace(nick, "{TENGO}", tengoNick, -1) //nolint:gocritic | ||||
| 	nick = strings.ReplaceAll(nick, "{TENGO}", tengoNick) | ||||
| 	return nick | ||||
| } | ||||
|  | ||||
| @@ -363,14 +365,28 @@ func (gw *Gateway) modifyAvatar(msg *config.Message, dest *bridge.Bridge) string | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) modifyMessage(msg *config.Message) { | ||||
| 	if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil { | ||||
| 	if gw.BridgeValues().General.TengoModifyMessage != "" { | ||||
| 		gw.logger.Warnf("General TengoModifyMessage=%s is deprecated and will be removed in v1.20.0, please move to Tengo InMessage=%s", gw.BridgeValues().General.TengoModifyMessage, gw.BridgeValues().General.TengoModifyMessage) | ||||
| 	} | ||||
|  | ||||
| 	if err := modifyInMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil { | ||||
| 		gw.logger.Errorf("TengoModifyMessage failed: %s", err) | ||||
| 	} | ||||
| 	if err := modifyMessageTengo(gw.BridgeValues().Tengo.Message, msg); err != nil { | ||||
|  | ||||
| 	inMessage := gw.BridgeValues().Tengo.InMessage | ||||
| 	if inMessage == "" { | ||||
| 		inMessage = gw.BridgeValues().Tengo.Message | ||||
| 		if inMessage != "" { | ||||
| 			gw.logger.Warnf("Tengo Message=%s is deprecated and will be removed in v1.20.0, please move to Tengo InMessage=%s", inMessage, inMessage) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := modifyInMessageTengo(inMessage, msg); err != nil { | ||||
| 		gw.logger.Errorf("Tengo.Message failed: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	// replace :emoji: to unicode | ||||
| 	emoji.ReplacePadding = "" | ||||
| 	msg.Text = emoji.Sprint(msg.Text) | ||||
|  | ||||
| 	br := gw.Bridges[msg.Account] | ||||
| @@ -416,9 +432,15 @@ func (gw *Gateway) SendMessage( | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Only send irc notices to irc | ||||
| 	if msg.Event == config.EventNoticeIRC && dest.Protocol != "irc" { | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	// Too noisy to log like other events | ||||
| 	debugSendMessage := "" | ||||
| 	if msg.Event != config.EventUserTyping { | ||||
| 		gw.logger.Debugf("=> Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, rmsg.Channel, dest.Account, channel.Name) | ||||
| 		debugSendMessage = fmt.Sprintf("=> Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, rmsg.Channel, dest.Account, channel.Name) | ||||
| 	} | ||||
|  | ||||
| 	msg.Channel = channel.Name | ||||
| @@ -438,22 +460,34 @@ func (gw *Gateway) SendMessage( | ||||
| 	} | ||||
|  | ||||
| 	// if the parentID is still empty and we have a parentID set in the original message | ||||
| 	// this means that we didn't find it in the cache so set it "msg-parent-not-found" | ||||
| 	// this means that we didn't find it in the cache so set it to a "msg-parent-not-found" constant | ||||
| 	if msg.ParentID == "" && rmsg.ParentID != "" { | ||||
| 		msg.ParentID = "msg-parent-not-found" | ||||
| 		msg.ParentID = config.ParentIDNotFound | ||||
| 	} | ||||
|  | ||||
| 	err := gw.modifySendMessageTengo(rmsg, &msg, dest) | ||||
| 	drop, err := gw.modifyOutMessageTengo(rmsg, &msg, dest) | ||||
| 	if err != nil { | ||||
| 		gw.logger.Errorf("modifySendMessageTengo: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	if drop { | ||||
| 		gw.logger.Debugf("=> Tengo dropping %#v from %s (%s) to %s (%s)", msg, msg.Account, rmsg.Channel, dest.Account, channel.Name) | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	if debugSendMessage != "" { | ||||
| 		gw.logger.Debug(debugSendMessage) | ||||
| 	} | ||||
| 	// if we are using mattermost plugin account, send messages to MattermostPlugin channel | ||||
| 	// that can be picked up by the mattermost matterbridge plugin | ||||
| 	if dest.Account == "mattermost.plugin" { | ||||
| 		gw.Router.MattermostPlugin <- msg | ||||
| 	} | ||||
|  | ||||
| 	defer func(t time.Time) { | ||||
| 		gw.logger.Debugf("=> Send from %s (%s) to %s (%s) took %s", msg.Account, rmsg.Channel, dest.Account, channel.Name, time.Since(t)) | ||||
| 	}(time.Now()) | ||||
|  | ||||
| 	mID, err := dest.Send(msg) | ||||
| 	if err != nil { | ||||
| 		return mID, err | ||||
| @@ -463,7 +497,7 @@ func (gw *Gateway) SendMessage( | ||||
| 	if mID != "" { | ||||
| 		gw.logger.Debugf("mID %s: %s", dest.Account, mID) | ||||
| 		return mID, nil | ||||
| 		//brMsgIDs = append(brMsgIDs, &BrMsgID{dest, dest.Protocol + " " + mID, channel.ID}) | ||||
| 		// brMsgIDs = append(brMsgIDs, &BrMsgID{dest, dest.Protocol + " " + mID, channel.ID}) | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
| @@ -505,7 +539,7 @@ func getProtocol(msg *config.Message) string { | ||||
| 	return p[0] | ||||
| } | ||||
|  | ||||
| func modifyMessageTengo(filename string, msg *config.Message) error { | ||||
| func modifyInMessageTengo(filename string, msg *config.Message) error { | ||||
| 	if filename == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| @@ -517,6 +551,7 @@ func modifyMessageTengo(filename string, msg *config.Message) error { | ||||
| 	s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) | ||||
| 	_ = s.Add("msgText", msg.Text) | ||||
| 	_ = s.Add("msgUsername", msg.Username) | ||||
| 	_ = s.Add("msgUserID", msg.UserID) | ||||
| 	_ = s.Add("msgAccount", msg.Account) | ||||
| 	_ = s.Add("msgChannel", msg.Channel) | ||||
| 	c, err := s.Compile() | ||||
| @@ -545,6 +580,7 @@ func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) ( | ||||
| 	_ = s.Add("result", "") | ||||
| 	_ = s.Add("msgText", msg.Text) | ||||
| 	_ = s.Add("msgUsername", msg.Username) | ||||
| 	_ = s.Add("msgUserID", msg.UserID) | ||||
| 	_ = s.Add("nick", msg.Username) | ||||
| 	_ = s.Add("msgAccount", msg.Account) | ||||
| 	_ = s.Add("msgChannel", msg.Channel) | ||||
| @@ -564,22 +600,28 @@ func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) ( | ||||
| 	return c.Get("result").String(), nil | ||||
| } | ||||
|  | ||||
| func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.Message, br *bridge.Bridge) error { | ||||
| func (gw *Gateway) modifyOutMessageTengo(origmsg *config.Message, msg *config.Message, br *bridge.Bridge) (bool, error) { | ||||
| 	filename := gw.BridgeValues().Tengo.OutMessage | ||||
| 	var res []byte | ||||
| 	var err error | ||||
| 	var ( | ||||
| 		res  []byte | ||||
| 		err  error | ||||
| 		drop bool | ||||
| 	) | ||||
|  | ||||
| 	if filename == "" { | ||||
| 		res, err = internal.Asset("tengo/outmessage.tengo") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 			return drop, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		res, err = ioutil.ReadFile(filename) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 			return drop, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	s := tengo.NewScript(res) | ||||
|  | ||||
| 	s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) | ||||
| 	_ = s.Add("inAccount", origmsg.Account) | ||||
| 	_ = s.Add("inProtocol", origmsg.Protocol) | ||||
| @@ -593,14 +635,20 @@ func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.M | ||||
| 	_ = s.Add("outEvent", msg.Event) | ||||
| 	_ = s.Add("msgText", msg.Text) | ||||
| 	_ = s.Add("msgUsername", msg.Username) | ||||
| 	_ = s.Add("msgUserID", msg.UserID) | ||||
| 	_ = s.Add("msgDrop", drop) | ||||
| 	c, err := s.Compile() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return drop, err | ||||
| 	} | ||||
|  | ||||
| 	if err := c.Run(); err != nil { | ||||
| 		return err | ||||
| 		return drop, err | ||||
| 	} | ||||
|  | ||||
| 	drop = c.Get("msgDrop").Bool() | ||||
| 	msg.Text = c.Get("msgText").String() | ||||
| 	msg.Username = c.Get("msgUsername").String() | ||||
| 	return nil | ||||
|  | ||||
| 	return drop, nil | ||||
| } | ||||
|   | ||||
| @@ -533,7 +533,7 @@ func (s *ignoreTestSuite) TestIgnoreNicks() { | ||||
| func BenchmarkTengo(b *testing.B) { | ||||
| 	msg := &config.Message{Username: "user", Text: "blah testing", Account: "protocol.account", Channel: "mychannel"} | ||||
| 	for n := 0; n < b.N; n++ { | ||||
| 		err := modifyMessageTengo("bench.tengo", msg) | ||||
| 		err := modifyInMessageTengo("bench.tengo", msg) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
|   | ||||
| @@ -160,7 +160,7 @@ func (r *Router) handleReceive() { | ||||
| 				// For some bridges we always add/update the message ID. | ||||
| 				// This is necessary as msgIDs will change if a bridge returns | ||||
| 				// a different ID in response to edits. | ||||
| 				if !exists || msg.Protocol == "discord" { | ||||
| 				if !exists { | ||||
| 					gw.Messages.Add(msg.Protocol+" "+msg.ID, msgIDs) | ||||
| 				} | ||||
| 			} | ||||
|   | ||||
							
								
								
									
										80
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,63 +3,59 @@ module github.com/42wim/matterbridge | ||||
| require ( | ||||
| 	github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 | ||||
| 	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.20200421062035-31e8111ac334 | ||||
| 	github.com/d5/tengo/v2 v2.4.2 | ||||
| 	github.com/Jeffail/gabs v1.4.0 // indirect | ||||
| 	github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560 | ||||
| 	github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce | ||||
| 	github.com/SevereCloud/vksdk/v2 v2.9.0 | ||||
| 	github.com/d5/tengo/v2 v2.7.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 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/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81 | ||||
| 	github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8 | ||||
| 	github.com/google/gops v0.3.17 | ||||
| 	github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect | ||||
| 	github.com/gorilla/schema v1.1.0 | ||||
| 	github.com/gorilla/schema v1.2.0 | ||||
| 	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-20200505163032-5cacf52379da | ||||
| 	github.com/labstack/echo/v4 v4.1.16 | ||||
| 	github.com/kyokomi/emoji/v2 v2.2.8 | ||||
| 	github.com/labstack/echo/v4 v4.2.1 | ||||
| 	github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 | ||||
| 	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 | ||||
| 	github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 | ||||
| 	github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible | ||||
| 	github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 | ||||
| 	github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20210403163225-761e8622445d | ||||
| 	github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7 | ||||
| 	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/gozulipbot v0.0.0-20200820220548-be5824faa913 | ||||
| 	github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba | ||||
| 	github.com/mattermost/mattermost-server v5.5.0+incompatible | ||||
| 	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/mattermost/mattermost-server/v5 v5.30.1 | ||||
| 	github.com/mattn/godown v0.0.1 | ||||
| 	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect | ||||
| 	github.com/missdeer/golib v1.0.4 | ||||
| 	github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d // indirect | ||||
| 	github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect | ||||
| 	github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 | ||||
| 	github.com/nicksnyder/go-i18n v1.4.0 // indirect | ||||
| 	github.com/onsi/ginkgo v1.6.0 // indirect | ||||
| 	github.com/onsi/gomega v1.4.1 // indirect | ||||
| 	github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c | ||||
| 	github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect | ||||
| 	github.com/rs/xid v1.2.1 | ||||
| 	github.com/russross/blackfriday v1.5.2 | ||||
| 	github.com/rs/xid v1.3.0 | ||||
| 	github.com/russross/blackfriday v1.6.0 | ||||
| 	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.6.0 | ||||
| 	github.com/slack-go/slack v0.6.4 | ||||
| 	github.com/spf13/viper v1.7.0 | ||||
| 	github.com/stretchr/testify v1.5.1 | ||||
| 	github.com/technoweenie/multipartstreamer v1.0.1 // indirect | ||||
| 	github.com/shazow/ssh-chat v1.10.1 | ||||
| 	github.com/sirupsen/logrus v1.8.1 | ||||
| 	github.com/slack-go/slack v0.8.2 | ||||
| 	github.com/spf13/afero v1.3.4 // indirect | ||||
| 	github.com/spf13/cast v1.3.1 // indirect | ||||
| 	github.com/spf13/viper v1.7.1 | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50 | ||||
| 	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-20190304061218-d34796e0a6c2 | ||||
| 	golang.org/x/image v0.0.0-20200430140353-33d19683fad8 | ||||
| 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | ||||
| 	gopkg.in/fsnotify.v1 v1.4.7 // indirect | ||||
| 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | ||||
| 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect | ||||
| 	github.com/yaegashi/msgraph.go v0.1.4 | ||||
| 	github.com/zfjagann/golang-ring v0.0.0-20210116075443-7c86fdb43134 | ||||
| 	golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb | ||||
| 	golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 | ||||
| 	gomod.garykim.dev/nc-talk v0.1.7 | ||||
| 	gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 | ||||
| 	layeh.com/gumble v0.0.0-20200818122324-146f9205029b | ||||
| ) | ||||
|  | ||||
| //replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 | ||||
|  | ||||
| go 1.13 | ||||
| go 1.15 | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	version = "1.17.5" | ||||
| 	version = "1.22.1" | ||||
| 	githash string | ||||
|  | ||||
| 	flagConfig  = flag.String("conf", "matterbridge.toml", "config file") | ||||
| @@ -51,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) | ||||
|   | ||||
| @@ -103,6 +103,12 @@ ColorNicks=false | ||||
| #OPTIONAL (default empty) | ||||
| RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"] | ||||
|  | ||||
| #PingDelay specifies how long to wait to send a ping to the irc server. | ||||
| #You can use s for second, m for minute | ||||
| #String | ||||
| #OPTIONAL (default 1m) | ||||
| PingDelay="1m" | ||||
|  | ||||
| #StripMarkdown strips markdown from messages | ||||
| #OPTIONAL (default false) | ||||
| StripMarkdown=false | ||||
| @@ -187,6 +193,19 @@ ShowTopicChange=false | ||||
| #OPTIONAL (default 0) | ||||
| JoinDelay=0 | ||||
|  | ||||
| #Use the optional RELAYMSG extension for username spoofing on IRC. | ||||
| #This requires an IRCd that supports the draft/relaymsg specification: currently this includes | ||||
| #Oragono 2.4.0+ and InspIRCd 3 with the m_relaymsg contrib module. | ||||
| #See https://github.com/42wim/matterbridge/issues/667#issuecomment-634214165 for more details. | ||||
| #Spoofed nicks will use the configured RemoteNickFormat, replacing reserved IRC characters | ||||
| #(!+%@&#$:'"?*,.) with a hyphen (-). | ||||
| #On most configurations, the RemoteNickFormat must include a separator character such as "/". | ||||
| #You should make sure that the settings here match your IRCd. | ||||
| #This option overrides ColorNicks. | ||||
| #OPTIONAL (default false) | ||||
| UseRelayMsg=false | ||||
| #RemoteNickFormat="{NICK}/{PROTOCOL}" | ||||
|  | ||||
| ################################################################### | ||||
| #XMPP section | ||||
| ################################################################### | ||||
| @@ -221,6 +240,10 @@ Nick="xmppbot" | ||||
| #OPTIONAL (default false) | ||||
| SkipTLSVerify=true | ||||
|  | ||||
| #Enable to use plaintext connection to your XMPP server. | ||||
| #OPTIONAL (default false) | ||||
| NoTLS=true | ||||
|  | ||||
| ## RELOADABLE SETTINGS | ||||
| ## Settings below can be reloaded by editing the file | ||||
|  | ||||
| @@ -287,6 +310,11 @@ StripNick=false | ||||
| #OPTIONAL (default false) | ||||
| ShowTopicChange=false | ||||
|  | ||||
| #Enable sending messages using a webhook instead of regular MUC messages. | ||||
| #Only works with a prosody server using mod_slack_webhook. Does not support editing. | ||||
| #OPTIONAL (default "") | ||||
| WebhookURL="https://yourdomain/prosody/msg/someid" | ||||
|  | ||||
| ################################################################### | ||||
| #mattermost section | ||||
| ################################################################### | ||||
| @@ -836,10 +864,11 @@ UseUserName=false | ||||
| # UseDiscriminator appends the `#xxxx` discriminator when used with UseUserName | ||||
| UseDiscriminator=false | ||||
|  | ||||
| # WebhookURL sends messages in the style of puppets. | ||||
| # This only works if you have one discord channel, if you have multiple discord channels you'll have to specify it in the gateway config | ||||
| # Example: "https://discordapp.com/api/webhooks/1234/abcd_xyzw" | ||||
| WebhookURL="" | ||||
| # AutoWebhooks automatically configures message sending in the style of puppets. | ||||
| # This is an easier alternative to manually configuring "WebhookURL" for each gateway, | ||||
| # as turning this on will automatically load or create webhooks for each channel. | ||||
| # This feature requires the "Manage Webhooks" permission (either globally or as per-channel). | ||||
| AutoWebhooks=false | ||||
|  | ||||
| # EditDisable disables sending of edits to other bridges | ||||
| EditDisable=false | ||||
| @@ -1221,6 +1250,9 @@ HTMLDisable=false | ||||
| ## RELOADABLE SETTINGS | ||||
| ## Settings below can be reloaded by editing the file | ||||
|  | ||||
| # UseUserName shows the username instead of the server nickname | ||||
| UseUserName=false | ||||
|  | ||||
| #Whether to prefix messages from other bridges to matrix with the sender's nick. | ||||
| #Useful if username overrides for incoming webhooks isn't enabled on the | ||||
| #matrix server. If you set PrefixMessagesWithNick to true, each message | ||||
| @@ -1383,7 +1415,88 @@ StripNick=false | ||||
| #OPTIONAL (default false) | ||||
| ShowTopicChange=false | ||||
|  | ||||
| ################################################################### | ||||
| # | ||||
| # NCTalk (Nextcloud Talk) | ||||
| # | ||||
| ################################################################### | ||||
|  | ||||
| [nctalk.bridge] | ||||
|  | ||||
| # Url of your Nextcloud server | ||||
| Server = "https://cloud.youdomain.me" | ||||
|  | ||||
| # Enable to not verify the certificate on your Nextcloud server. | ||||
| # e.g. when using selfsigned certificates | ||||
| # OPTIONAL (default false) | ||||
| SkipTLSVerify=true | ||||
|  | ||||
| # Username of the bot | ||||
| Login = "talkuser" | ||||
|  | ||||
| # Password of the bot | ||||
| Password = "talkuserpass" | ||||
|  | ||||
| # Suffix for Guest Users | ||||
| GuestSuffix = " (Guest)" | ||||
|  | ||||
| ################################################################### | ||||
| # | ||||
| # Mumble | ||||
| # | ||||
| ################################################################### | ||||
|  | ||||
| [mumble.bridge] | ||||
|  | ||||
| # Host and port of your Mumble server | ||||
| Server = "mumble.yourdomain.me:64738" | ||||
|  | ||||
| # Nickname to log in as | ||||
| Nick = "matterbridge" | ||||
|  | ||||
| # Some servers require a password | ||||
| # OPTIONAL (default empty) | ||||
| Password = "serverpasswordhere" | ||||
|  | ||||
| # User comment to set on the Mumble user, visible to other users. | ||||
| # OPTIONAL (default empty) | ||||
| UserComment="I am bridging text messages between this channel and #general on irc.yourdomain.me" | ||||
|  | ||||
| # Self-signed TLS client certificate + private key used to connect to | ||||
| # Mumble.  This is required if you want to register the matterbridge | ||||
| # user on your Mumble server, so its nick becomes reserved. | ||||
| # You can generate a keypair using e.g. | ||||
| # | ||||
| #     openssl req -x509 -newkey rsa:2048 -nodes -days 10000 \ | ||||
| #             -keyout mumble.key -out mumble.crt | ||||
| # | ||||
| # To actually register the matterbridege user, connect to Mumble as an | ||||
| # admin, right click on the user and click "Register". | ||||
| # | ||||
| # OPTIONAL (default empty) | ||||
| TLSClientCertificate="mumble.crt" | ||||
| TLSClientKey="mumble.key" | ||||
|  | ||||
| # TLS CA certificate used to validate the Mumble server. | ||||
| # OPTIONAL (defaults to Go system CA) | ||||
| TLSCACertificate=mumble-ca.crt | ||||
|  | ||||
| # Enable to not verify the certificate on your Mumble server. | ||||
| # e.g. when using selfsigned certificates | ||||
| # OPTIONAL (default false) | ||||
| SkipTLSVerify=false | ||||
|  | ||||
| ################################################################### | ||||
| #VK | ||||
| ################################################################### | ||||
| [vk.myvk] | ||||
| #Group access token | ||||
| #See https://vk.com/dev/bots_docs | ||||
| Token="Yourtokenhere" | ||||
|  | ||||
| #Group ID | ||||
| #For example in URL https://vk.com/public168963511 group ID is 168963511 | ||||
| GroupID=123456789 | ||||
|  | ||||
| ################################################################### | ||||
| # | ||||
| @@ -1525,6 +1638,8 @@ Buffer=1000 | ||||
|  | ||||
| #Bearer token used for authentication | ||||
| #curl -H "Authorization: Bearer token" http://localhost:4242/api/messages | ||||
| # https://github.com/vi/websocat | ||||
| # websocat -H="Authorization: Bearer token" ws://127.0.0.1:4242/api/websocket | ||||
| #OPTIONAL (no authorization if token is empty) | ||||
| Token="mytoken" | ||||
|  | ||||
| @@ -1548,7 +1663,8 @@ RemoteNickFormat="{NICK}" | ||||
| ## Settings below can be reloaded by editing the file | ||||
|  | ||||
| #RemoteNickFormat defines how remote users appear on this bridge | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick / username. | ||||
| #The string "{NICK}" (case sensitive) will be replaced by the actual nick. | ||||
| #The string "{USERID}" (case sensitive) will be replaced by the user ID. | ||||
| #The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge | ||||
| #The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge | ||||
| #The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge | ||||
| @@ -1604,6 +1720,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 | ||||
| ################################################################### | ||||
| @@ -1615,7 +1739,7 @@ IgnoreFailureOnStart=false | ||||
| #This script will receive every incoming message and can be used to modify the Username and the Text of that message. | ||||
| #The script will have the following global variables: | ||||
| #to modify: msgUsername and msgText | ||||
| #to read: msgChannel and msgAccount | ||||
| #to read: msgUserID, msgChannel, msgAccount | ||||
| # | ||||
| #The script is reloaded on every message, so you can modify the script on the fly. | ||||
| # | ||||
| @@ -1639,9 +1763,12 @@ InMessage="example.tengo" | ||||
| #read-only: | ||||
| #inAccount, inProtocol, inChannel, inGateway, inEvent | ||||
| #outAccount, outProtocol, outChannel, outGateway, outEvent | ||||
| #msgUserID | ||||
| # | ||||
| #read-write: | ||||
| #msgText, msgUsername | ||||
| #msgText, msgUsername, msgDrop | ||||
| # | ||||
| #msgDrop is a bool which is default false, when set true this message will be dropped | ||||
| # | ||||
| #The script is reloaded on every message, so you can modify the script on the fly. | ||||
| # | ||||
| @@ -1654,7 +1781,7 @@ OutMessage="example.tengo" | ||||
| #RemoteNickFormat allows you to specify the location of a tengo (https://github.com/d5/tengo/) script. | ||||
| #The script will have the following global variables: | ||||
| #to modify: result | ||||
| #to read: channel, bridge, gateway, protocol, nick | ||||
| #to read: channel, bridge, gateway, protocol, nick, msgUserID | ||||
| # | ||||
| #The result will be set in {TENGO} in the RemoteNickFormat key of every bridge where {TENGO} is specified | ||||
| # | ||||
| @@ -1694,7 +1821,7 @@ enable=true | ||||
|     # REQUIRED | ||||
|     account="irc.freenode" | ||||
|  | ||||
|     # The channel key in each gateway is mapped to a similar group chat ID on the chat platform  | ||||
|     # The channel key in each gateway is mapped to a similar group chat ID on the chat platform | ||||
|     # To find the group chat ID for different platforms, refer to the table below | ||||
|     # | ||||
|     # Platform   |   Identifier name  |            Example            | Description | ||||
| @@ -1715,15 +1842,21 @@ enable=true | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     #   msteams  |      threadId      |    19:82abcxx@thread.skype    | You'll find the threadId in the URL | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     #   mumble   |    channel id      |              42               | The channel ID, as shown in the channel's "Edit" window | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     # rocketchat |      channel       |            #channel           | # is required for private channels too | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     #   slack    |   channel name     |            general            | Do not include the # symbol | ||||
|     #            |    channel id      |           ID:C123456          | The underlying ID of a channel. This doesn't work with | ||||
|     #            |    channel id      |           ID:C123456          | The underlying ID of a channel. This doesn't work with webhooks. | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     #   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 | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     #  vk        |      peerid        |          2000000002           | A number that starts form 2000000000. Use --debug and send any message in chat to get PeerID in the logs  | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
|     #  whatsapp  |     group JID      | 48111222333-123455678999@g.us | A unique group JID. If you specify an empty string, bridge will list all the possibilities | ||||
|     #            |    "Group Name"    |         "Family Chat"         | if you specify a group name, the bridge will find hint the JID to specify. Names can change over time and are not stable. | ||||
|     # ------------------------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -1763,13 +1896,16 @@ enable=true | ||||
|         #OPTIONAL - your irc / xmpp channel key | ||||
|         key="yourkey" | ||||
|  | ||||
|     # Discord specific gateway options | ||||
|     [[gateway.inout]] | ||||
|     account="discord.game" | ||||
|     channel="mygreatgame" | ||||
|  | ||||
|         #OPTIONAL - webhookurl only works for discord (it needs a different URL for each cahnnel) | ||||
|         [gateway.inout.options] | ||||
|         webhookurl="https://discordapp.com/api/webhooks/123456789123456789/C9WPqExYWONPDZabcdef-def1434FGFjstasJX9pYht73y" | ||||
|         # WebhookURL sends messages in the style of "puppets". You must configure a webhook URL for each channel you want to bridge. | ||||
|         # If you have more than one channel and don't wnat to configure each channel manually, see the "AutoWebhooks" option in the gateway config. | ||||
|         # Example: "https://discord.com/api/webhooks/1234/abcd_xyzw" | ||||
|         WebhookURL="" | ||||
|  | ||||
|     [[gateway.inout]] | ||||
|     account="zulip.streamchat" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| ) | ||||
|  | ||||
| // GetChannels returns all channels we're members off | ||||
| @@ -167,7 +167,7 @@ func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint | ||||
| } | ||||
|  | ||||
| func (m *MMClient) UpdateChannelsTeam(teamID string) error { | ||||
| 	mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "") | ||||
| 	mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, false, "") | ||||
| 	if resp.Error != nil { | ||||
| 		return errors.New(resp.Error.DetailedError) | ||||
| 	} | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import ( | ||||
|  | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/jpillora/backoff" | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| ) | ||||
|  | ||||
| func (m *MMClient) doLogin(firstConnection bool, b *backoff.Backoff) error { | ||||
| @@ -154,7 +154,7 @@ func (m *MMClient) initUser() error { | ||||
|  | ||||
| 		t := &Team{Team: team, Users: usermap, Id: team.Id} | ||||
|  | ||||
| 		mmchannels, resp := m.Client.GetChannelsForTeamForUser(team.Id, m.User.Id, "") | ||||
| 		mmchannels, resp := m.Client.GetChannelsForTeamForUser(team.Id, m.User.Id, false, "") | ||||
| 		if resp.Error != nil { | ||||
| 			return resp.Error | ||||
| 		} | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import ( | ||||
| 	lru "github.com/hashicorp/golang-lru" | ||||
| 	"github.com/jpillora/backoff" | ||||
| 	prefixed "github.com/matterbridge/logrus-prefixed-formatter" | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| @@ -69,6 +69,7 @@ type MMClient struct { | ||||
| 	logger     *logrus.Entry | ||||
| 	rootLogger *logrus.Logger | ||||
| 	lruCache   *lru.Cache | ||||
| 	allevents  bool | ||||
| } | ||||
|  | ||||
| // New will instantiate a new Matterclient with the specified login details without connecting. | ||||
| @@ -119,6 +120,10 @@ func (m *MMClient) SetLogLevel(level string) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (m *MMClient) EnableAllEvents() { | ||||
| 	m.allevents = true | ||||
| } | ||||
|  | ||||
| // Login tries to connect the client with the loging details with which it was initialized. | ||||
| func (m *MMClient) Login() error { | ||||
| 	// check if this is a first connect or a reconnection | ||||
| @@ -220,6 +225,10 @@ func (m *MMClient) WsReceiver() { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			if m.allevents { | ||||
| 				m.MessageChan <- msg | ||||
| 				continue | ||||
| 			} | ||||
| 			switch msg.Raw.Event { | ||||
| 			case model.WEBSOCKET_EVENT_USER_ADDED, | ||||
| 				model.WEBSOCKET_EVENT_USER_REMOVED, | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package matterclient | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| ) | ||||
|  | ||||
| func (m *MMClient) parseActionPost(rmsg *Message) { | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-server/model" | ||||
| 	"github.com/mattermost/mattermost-server/v5/model" | ||||
| ) | ||||
|  | ||||
| func (m *MMClient) GetNickName(userId string) string { //nolint:golint | ||||
|   | ||||
							
								
								
									
										38
									
								
								tgs.Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tgs.Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| FROM alpine AS builder | ||||
|  | ||||
| COPY . /go/src/github.com/42wim/matterbridge | ||||
| RUN apk add \ | ||||
|     go \ | ||||
|     git \ | ||||
|     gcc \ | ||||
|     musl-dev \ | ||||
|   && cd /go/src/github.com/42wim/matterbridge \ | ||||
|   && export GOPATH=/go \ | ||||
|   && go get \ | ||||
|   && go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge | ||||
|  | ||||
| FROM alpine | ||||
| RUN apk --no-cache add \ | ||||
|     ca-certificates \ | ||||
|     cairo \ | ||||
|     libjpeg-turbo \ | ||||
|     mailcap \ | ||||
|     py3-webencodings \ | ||||
|     python3 \ | ||||
|   && apk --no-cache add --virtual .compile \ | ||||
|     gcc \ | ||||
|     libffi-dev \ | ||||
|     libjpeg-turbo-dev \ | ||||
|     musl-dev \ | ||||
|     py3-pip \ | ||||
|     py3-wheel \ | ||||
|     python3-dev \ | ||||
|     zlib-dev \ | ||||
|   && pip3 install --no-cache-dir lottie[PNG] \ | ||||
|   && apk --no-cache del .compile | ||||
|  | ||||
| COPY --from=builder /bin/matterbridge /bin/matterbridge | ||||
| RUN mkdir /etc/matterbridge \ | ||||
|   && touch /etc/matterbridge/matterbridge.toml \ | ||||
|   && ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml | ||||
| ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"] | ||||
							
								
								
									
										77
									
								
								vendor/github.com/Jeffail/gabs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/Jeffail/gabs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,21 +7,17 @@ It does nothing spectacular except for being fabulous. | ||||
|  | ||||
| https://godoc.org/github.com/Jeffail/gabs | ||||
|  | ||||
| ## How to install: | ||||
| ## Install | ||||
|  | ||||
| ``` bash | ||||
| go get github.com/Jeffail/gabs | ||||
| ``` | ||||
|  | ||||
| ## How to use | ||||
| ## Use | ||||
|  | ||||
| ### Parsing and searching JSON | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| import "github.com/Jeffail/gabs" | ||||
|  | ||||
| jsonParsed, err := gabs.ParseJSON([]byte(`{ | ||||
| 	"outter":{ | ||||
| 		"inner":{ | ||||
| @@ -29,7 +25,10 @@ jsonParsed, err := gabs.ParseJSON([]byte(`{ | ||||
| 			"value2":22 | ||||
| 		}, | ||||
| 		"alsoInner":{ | ||||
| 			"value1":20 | ||||
| 			"value1":20, | ||||
| 			"array1":[ | ||||
| 				30, 40 | ||||
| 			] | ||||
| 		} | ||||
| 	} | ||||
| }`)) | ||||
| @@ -43,26 +42,26 @@ value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64) | ||||
| value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64) | ||||
| // value == 10.0, ok == true | ||||
|  | ||||
| gObj, err := jsonParsed.JSONPointer("/outter/alsoInner/array1/1") | ||||
| if err != nil { | ||||
| 	panic(err) | ||||
| } | ||||
| value, ok = gObj.Data().(float64) | ||||
| // value == 40.0, ok == true | ||||
|  | ||||
| value, ok = jsonParsed.Path("does.not.exist").Data().(float64) | ||||
| // value == 0.0, ok == false | ||||
|  | ||||
| exists := jsonParsed.Exists("outter", "inner", "value1") | ||||
| // exists == true | ||||
|  | ||||
| exists := jsonParsed.Exists("does", "not", "exist") | ||||
| // exists == false | ||||
|  | ||||
| exists := jsonParsed.ExistsP("does.not.exist") | ||||
| // exists == false | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| ### Iterating objects | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonParsed, _ := gabs.ParseJSON([]byte(`{"object":{ "first": 1, "second": 2, "third": 3 }}`)) | ||||
|  | ||||
| // S is shorthand for Search | ||||
| @@ -70,24 +69,25 @@ children, _ := jsonParsed.S("object").ChildrenMap() | ||||
| for key, child := range children { | ||||
| 	fmt.Printf("key: %v, value: %v\n", key, child.Data().(string)) | ||||
| } | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| ### Iterating arrays | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonParsed, _ := gabs.ParseJSON([]byte(`{"array":[ "first", "second", "third" ]}`)) | ||||
| jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[ "first", "second", "third" ]}`)) | ||||
| if err != nil { | ||||
| 	panic(err) | ||||
| } | ||||
|  | ||||
| // S is shorthand for Search | ||||
| children, _ := jsonParsed.S("array").Children() | ||||
| children, err := jsonParsed.S("array").Children() | ||||
| if err != nil { | ||||
| 	panic(err) | ||||
| } | ||||
|  | ||||
| for _, child := range children { | ||||
| 	fmt.Println(child.Data().(string)) | ||||
| } | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| Will print: | ||||
| @@ -108,12 +108,11 @@ objects within the array, this returns a JSON array containing the results for | ||||
| each element. | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonParsed, _ := gabs.ParseJSON([]byte(`{"array":[ {"value":1}, {"value":2}, {"value":3} ]}`)) | ||||
| jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[ {"value":1}, {"value":2}, {"value":3} ]}`)) | ||||
| if err != nil { | ||||
| 	panic(err) | ||||
| } | ||||
| fmt.Println(jsonParsed.Path("array.value").String()) | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| Will print: | ||||
| @@ -125,8 +124,6 @@ Will print: | ||||
| ### Generating JSON | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonObj := gabs.New() | ||||
| // or gabs.Consume(jsonObject) to work on an existing map[string]interface{} | ||||
|  | ||||
| @@ -135,8 +132,6 @@ jsonObj.SetP(20, "outter.inner.value2") | ||||
| jsonObj.Set(30, "outter", "inner2", "value3") | ||||
|  | ||||
| fmt.Println(jsonObj.String()) | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| Will print: | ||||
| @@ -148,11 +143,7 @@ Will print: | ||||
| To pretty-print: | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| fmt.Println(jsonObj.StringIndent("", "  ")) | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| Will print: | ||||
| @@ -174,8 +165,6 @@ Will print: | ||||
| ### Generating Arrays | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonObj := gabs.New() | ||||
|  | ||||
| jsonObj.Array("foo", "array") | ||||
| @@ -186,8 +175,6 @@ jsonObj.ArrayAppend(20, "foo", "array") | ||||
| jsonObj.ArrayAppend(30, "foo", "array") | ||||
|  | ||||
| fmt.Println(jsonObj.String()) | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| Will print: | ||||
| @@ -199,8 +186,6 @@ Will print: | ||||
| Working with arrays by index: | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonObj := gabs.New() | ||||
|  | ||||
| // Create an array with the length of 3 | ||||
| @@ -217,8 +202,6 @@ jsonObj.S("foo").Index(2).SetIndex(2, 1) | ||||
| jsonObj.S("foo").Index(2).SetIndex(3, 2) | ||||
|  | ||||
| fmt.Println(jsonObj.String()) | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| Will print: | ||||
| @@ -232,8 +215,6 @@ Will print: | ||||
| This is the easiest part: | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonParsedObj, _ := gabs.ParseJSON([]byte(`{ | ||||
| 	"outter":{ | ||||
| 		"values":{ | ||||
| @@ -246,15 +227,11 @@ jsonParsedObj, _ := gabs.ParseJSON([]byte(`{ | ||||
|  | ||||
| jsonOutput := jsonParsedObj.String() | ||||
| // Becomes `{"outter":{"values":{"first":10,"second":11}},"outter2":"hello world"}` | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| And to serialize a specific segment is as simple as: | ||||
|  | ||||
| ``` go | ||||
| ... | ||||
|  | ||||
| jsonParsedObj := gabs.ParseJSON([]byte(`{ | ||||
| 	"outter":{ | ||||
| 		"values":{ | ||||
| @@ -267,8 +244,6 @@ jsonParsedObj := gabs.ParseJSON([]byte(`{ | ||||
|  | ||||
| jsonOutput := jsonParsedObj.Search("outter").String() | ||||
| // Becomes `{"values":{"first":10,"second":11}}` | ||||
|  | ||||
| ... | ||||
| ``` | ||||
|  | ||||
| ### Merge two containers | ||||
|   | ||||
							
								
								
									
										344
									
								
								vendor/github.com/Jeffail/gabs/gabs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										344
									
								
								vendor/github.com/Jeffail/gabs/gabs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -20,58 +20,85 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
| */ | ||||
|  | ||||
| // Package gabs implements a simplified wrapper around creating and parsing JSON. | ||||
| // Package gabs implements a simplified wrapper around creating and parsing | ||||
| // unknown or dynamic JSON. | ||||
| package gabs | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| var ( | ||||
| 	// ErrOutOfBounds - Index out of bounds. | ||||
| 	// ErrOutOfBounds indicates an index was out of bounds. | ||||
| 	ErrOutOfBounds = errors.New("out of bounds") | ||||
|  | ||||
| 	// ErrNotObjOrArray - The target is not an object or array type. | ||||
| 	// ErrNotObjOrArray is returned when a target is not an object or array type | ||||
| 	// but needs to be for the intended operation. | ||||
| 	ErrNotObjOrArray = errors.New("not an object or array") | ||||
|  | ||||
| 	// ErrNotObj - The target is not an object type. | ||||
| 	// ErrNotObj is returned when a target is not an object but needs to be for | ||||
| 	// the intended operation. | ||||
| 	ErrNotObj = errors.New("not an object") | ||||
|  | ||||
| 	// ErrNotArray - The target is not an array type. | ||||
| 	// ErrNotArray is returned when a target is not an array but needs to be for | ||||
| 	// the intended operation. | ||||
| 	ErrNotArray = errors.New("not an array") | ||||
|  | ||||
| 	// ErrPathCollision - Creating a path failed because an element collided with an existing value. | ||||
| 	// ErrPathCollision is returned when creating a path failed because an | ||||
| 	// element collided with an existing value. | ||||
| 	ErrPathCollision = errors.New("encountered value collision whilst building path") | ||||
|  | ||||
| 	// ErrInvalidInputObj - The input value was not a map[string]interface{}. | ||||
| 	// ErrInvalidInputObj is returned when the input value was not a | ||||
| 	// map[string]interface{}. | ||||
| 	ErrInvalidInputObj = errors.New("invalid input object") | ||||
|  | ||||
| 	// ErrInvalidInputText - The input data could not be parsed. | ||||
| 	// ErrInvalidInputText is returned when the input data could not be parsed. | ||||
| 	ErrInvalidInputText = errors.New("input text could not be parsed") | ||||
|  | ||||
| 	// ErrInvalidPath - The filepath was not valid. | ||||
| 	// ErrInvalidPath is returned when the filepath was not valid. | ||||
| 	ErrInvalidPath = errors.New("invalid file path") | ||||
|  | ||||
| 	// ErrInvalidBuffer - The input buffer contained an invalid JSON string | ||||
| 	// ErrInvalidBuffer is returned when the input buffer contained an invalid | ||||
| 	// JSON string. | ||||
| 	ErrInvalidBuffer = errors.New("input buffer contained invalid JSON") | ||||
| ) | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| // Container - an internal structure that holds a reference to the core interface map of the parsed | ||||
| // json. Use this container to move context. | ||||
| func resolveJSONPointerHierarchy(path string) ([]string, error) { | ||||
| 	if len(path) < 1 { | ||||
| 		return nil, errors.New("failed to resolve JSON pointer: path must not be empty") | ||||
| 	} | ||||
| 	if path[0] != '/' { | ||||
| 		return nil, errors.New("failed to resolve JSON pointer: path must begin with '/'") | ||||
| 	} | ||||
| 	hierarchy := strings.Split(path, "/")[1:] | ||||
| 	for i, v := range hierarchy { | ||||
| 		v = strings.Replace(v, "~1", "/", -1) | ||||
| 		v = strings.Replace(v, "~0", "~", -1) | ||||
| 		hierarchy[i] = v | ||||
| 	} | ||||
| 	return hierarchy, nil | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| // Container references a specific element within a JSON structure. | ||||
| type Container struct { | ||||
| 	object interface{} | ||||
| } | ||||
|  | ||||
| // Data - Return the contained data as an interface{}. | ||||
| // Data returns the underlying interface{} of the target element in the JSON | ||||
| // structure. | ||||
| func (g *Container) Data() interface{} { | ||||
| 	if g == nil { | ||||
| 		return nil | ||||
| @@ -79,17 +106,18 @@ func (g *Container) Data() interface{} { | ||||
| 	return g.object | ||||
| } | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| // Path - Search for a value using dot notation. | ||||
| // Path searches the JSON structure following a path in dot notation. | ||||
| func (g *Container) Path(path string) *Container { | ||||
| 	return g.Search(strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // Search - Attempt to find and return an object within the JSON structure by specifying the | ||||
| // hierarchy of field names to locate the target. If the search encounters an array and has not | ||||
| // reached the end target then it will iterate each object of the array for the target and return | ||||
| // all of the results in a JSON array. | ||||
| // Search attempts to find and return an object within the JSON structure by | ||||
| // following a provided hierarchy of field names to locate the target. If the | ||||
| // search encounters an array and has not reached the end target then it will | ||||
| // iterate each object of the array for the target and return all of the results | ||||
| // in a JSON array. | ||||
| func (g *Container) Search(hierarchy ...string) *Container { | ||||
| 	var object interface{} | ||||
|  | ||||
| @@ -120,22 +148,55 @@ func (g *Container) Search(hierarchy ...string) *Container { | ||||
| 	return &Container{object} | ||||
| } | ||||
|  | ||||
| // S - Shorthand method, does the same thing as Search. | ||||
| // JSONPointer parses a JSON pointer path (https://tools.ietf.org/html/rfc6901) | ||||
| // and either returns a *gabs.Container containing the result or an error if the | ||||
| // referenced item could not be found. | ||||
| func (g *Container) JSONPointer(path string) (*Container, error) { | ||||
| 	hierarchy, err := resolveJSONPointerHierarchy(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	object := g.Data() | ||||
| 	for target := 0; target < len(hierarchy); target++ { | ||||
| 		pathSeg := hierarchy[target] | ||||
| 		if mmap, ok := object.(map[string]interface{}); ok { | ||||
| 			object, ok = mmap[pathSeg] | ||||
| 			if !ok { | ||||
| 				return nil, fmt.Errorf("failed to resolve JSON pointer: index '%v' value '%v' was not found", target, pathSeg) | ||||
| 			} | ||||
| 		} else if marray, ok := object.([]interface{}); ok { | ||||
| 			index, err := strconv.Atoi(pathSeg) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("failed to resolve JSON pointer: could not parse index '%v' value '%v' into array index: %v", target, pathSeg, err) | ||||
| 			} | ||||
| 			if len(marray) <= index { | ||||
| 				return nil, fmt.Errorf("failed to resolve JSON pointer: index '%v' value '%v' exceeded target array size of '%v'", target, pathSeg, len(marray)) | ||||
| 			} | ||||
| 			object = marray[index] | ||||
| 		} else { | ||||
| 			return &Container{nil}, fmt.Errorf("failed to resolve JSON pointer: index '%v' field '%v' was not found", target, pathSeg) | ||||
| 		} | ||||
| 	} | ||||
| 	return &Container{object}, nil | ||||
| } | ||||
|  | ||||
| // S is a shorthand alias for Search. | ||||
| func (g *Container) S(hierarchy ...string) *Container { | ||||
| 	return g.Search(hierarchy...) | ||||
| } | ||||
|  | ||||
| // Exists - Checks whether a path exists. | ||||
| // Exists checks whether a path exists. | ||||
| func (g *Container) Exists(hierarchy ...string) bool { | ||||
| 	return g.Search(hierarchy...) != nil | ||||
| } | ||||
|  | ||||
| // ExistsP - Checks whether a dot notation path exists. | ||||
| // ExistsP checks whether a dot notation path exists. | ||||
| func (g *Container) ExistsP(path string) bool { | ||||
| 	return g.Exists(strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // Index - Attempt to find and return an object within a JSON array by index. | ||||
| // Index attempts to find and return an element within a JSON array by an index. | ||||
| func (g *Container) Index(index int) *Container { | ||||
| 	if array, ok := g.Data().([]interface{}); ok { | ||||
| 		if index >= len(array) { | ||||
| @@ -146,9 +207,9 @@ func (g *Container) Index(index int) *Container { | ||||
| 	return &Container{nil} | ||||
| } | ||||
|  | ||||
| // Children - Return a slice of all the children of the array. This also works for objects, however, | ||||
| // the children returned for an object will NOT be in order and you lose the names of the returned | ||||
| // objects this way. | ||||
| // Children returns a slice of all children of an array element. This also works | ||||
| // for objects, however, the children returned for an object will be in a random | ||||
| // order and you lose the names of the returned objects this way. | ||||
| func (g *Container) Children() ([]*Container, error) { | ||||
| 	if array, ok := g.Data().([]interface{}); ok { | ||||
| 		children := make([]*Container, len(array)) | ||||
| @@ -167,7 +228,7 @@ func (g *Container) Children() ([]*Container, error) { | ||||
| 	return nil, ErrNotObjOrArray | ||||
| } | ||||
|  | ||||
| // ChildrenMap - Return a map of all the children of an object. | ||||
| // ChildrenMap returns a map of all the children of an object element. | ||||
| func (g *Container) ChildrenMap() (map[string]*Container, error) { | ||||
| 	if mmap, ok := g.Data().(map[string]interface{}); ok { | ||||
| 		children := map[string]*Container{} | ||||
| @@ -179,11 +240,11 @@ func (g *Container) ChildrenMap() (map[string]*Container, error) { | ||||
| 	return nil, ErrNotObj | ||||
| } | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| // Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be | ||||
| // constructed, and if a collision occurs with a non object type whilst iterating the path an error | ||||
| // is returned. | ||||
| // Set the value of a field at a JSON path, any parts of the path that do not | ||||
| // exist will be constructed, and if a collision occurs with a non object type | ||||
| // whilst iterating the path an error is returned. | ||||
| func (g *Container) Set(value interface{}, path ...string) (*Container, error) { | ||||
| 	if len(path) == 0 { | ||||
| 		g.object = value | ||||
| @@ -209,12 +270,14 @@ func (g *Container) Set(value interface{}, path ...string) (*Container, error) { | ||||
| 	return &Container{object}, nil | ||||
| } | ||||
|  | ||||
| // SetP - Does the same as Set, but using a dot notation JSON path. | ||||
| // SetP sets the value of a field at a JSON path using dot notation, any parts | ||||
| // of the path that do not exist will be constructed, and if a collision occurs | ||||
| // with a non object type whilst iterating the path an error is returned. | ||||
| func (g *Container) SetP(value interface{}, path string) (*Container, error) { | ||||
| 	return g.Set(value, strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // SetIndex - Set a value of an array element based on the index. | ||||
| // SetIndex attempts to set a value of an array element based on an index. | ||||
| func (g *Container) SetIndex(value interface{}, index int) (*Container, error) { | ||||
| 	if array, ok := g.Data().([]interface{}); ok { | ||||
| 		if index >= len(array) { | ||||
| @@ -226,60 +289,112 @@ func (g *Container) SetIndex(value interface{}, index int) (*Container, error) { | ||||
| 	return &Container{nil}, ErrNotArray | ||||
| } | ||||
|  | ||||
| // Object - Create a new JSON object at a path. Returns an error if the path contains a collision | ||||
| // with a non object type. | ||||
| // SetJSONPointer parses a JSON pointer path | ||||
| // (https://tools.ietf.org/html/rfc6901) and sets the leaf to a value. Returns | ||||
| // an error if the pointer could not be resolved due to missing fields. | ||||
| func (g *Container) SetJSONPointer(value interface{}, path string) error { | ||||
| 	hierarchy, err := resolveJSONPointerHierarchy(path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if len(hierarchy) == 0 { | ||||
| 		g.object = value | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	object := g.object | ||||
|  | ||||
| 	for target := 0; target < len(hierarchy); target++ { | ||||
| 		pathSeg := hierarchy[target] | ||||
| 		if mmap, ok := object.(map[string]interface{}); ok { | ||||
| 			if target == len(hierarchy)-1 { | ||||
| 				object = value | ||||
| 				mmap[pathSeg] = object | ||||
| 			} else if object = mmap[pathSeg]; object == nil { | ||||
| 				return fmt.Errorf("failed to resolve JSON pointer: index '%v' value '%v' was not found", target, pathSeg) | ||||
| 			} | ||||
| 		} else if marray, ok := object.([]interface{}); ok { | ||||
| 			index, err := strconv.Atoi(pathSeg) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("failed to resolve JSON pointer: could not parse index '%v' value '%v' into array index: %v", target, pathSeg, err) | ||||
| 			} | ||||
| 			if len(marray) <= index { | ||||
| 				return fmt.Errorf("failed to resolve JSON pointer: index '%v' value '%v' exceeded target array size of '%v'", target, pathSeg, len(marray)) | ||||
| 			} | ||||
| 			if target == len(hierarchy)-1 { | ||||
| 				object = value | ||||
| 				marray[index] = object | ||||
| 			} else if object = marray[index]; object == nil { | ||||
| 				return fmt.Errorf("failed to resolve JSON pointer: index '%v' value '%v' was not found", target, pathSeg) | ||||
| 			} | ||||
| 		} else { | ||||
| 			return fmt.Errorf("failed to resolve JSON pointer: index '%v' value '%v' was not found", target, pathSeg) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Object creates a new JSON object at a target path. Returns an error if the | ||||
| // path contains a collision with a non object type. | ||||
| func (g *Container) Object(path ...string) (*Container, error) { | ||||
| 	return g.Set(map[string]interface{}{}, path...) | ||||
| } | ||||
|  | ||||
| // ObjectP - Does the same as Object, but using a dot notation JSON path. | ||||
| // ObjectP creates a new JSON object at a target path using dot notation. | ||||
| // Returns an error if the path contains a collision with a non object type. | ||||
| func (g *Container) ObjectP(path string) (*Container, error) { | ||||
| 	return g.Object(strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an | ||||
| // array or the index is out of bounds. | ||||
| // ObjectI creates a new JSON object at an array index. Returns an error if the | ||||
| // object is not an array or the index is out of bounds. | ||||
| func (g *Container) ObjectI(index int) (*Container, error) { | ||||
| 	return g.SetIndex(map[string]interface{}{}, index) | ||||
| } | ||||
|  | ||||
| // Array - Create a new JSON array at a path. Returns an error if the path contains a collision with | ||||
| // a non object type. | ||||
| // Array creates a new JSON array at a path. Returns an error if the path | ||||
| // contains a collision with a non object type. | ||||
| func (g *Container) Array(path ...string) (*Container, error) { | ||||
| 	return g.Set([]interface{}{}, path...) | ||||
| } | ||||
|  | ||||
| // ArrayP - Does the same as Array, but using a dot notation JSON path. | ||||
| // ArrayP creates a new JSON array at a path using dot notation. Returns an | ||||
| // error if the path contains a collision with a non object type. | ||||
| func (g *Container) ArrayP(path string) (*Container, error) { | ||||
| 	return g.Array(strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an | ||||
| // array or the index is out of bounds. | ||||
| // ArrayI creates a new JSON array within an array at an index. Returns an error | ||||
| // if the element is not an array or the index is out of bounds. | ||||
| func (g *Container) ArrayI(index int) (*Container, error) { | ||||
| 	return g.SetIndex([]interface{}{}, index) | ||||
| } | ||||
|  | ||||
| // ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the | ||||
| // path contains a collision with a non object type. | ||||
| // ArrayOfSize creates a new JSON array of a particular size at a path. Returns | ||||
| // an error if the path contains a collision with a non object type. | ||||
| func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) { | ||||
| 	a := make([]interface{}, size) | ||||
| 	return g.Set(a, path...) | ||||
| } | ||||
|  | ||||
| // ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path. | ||||
| // ArrayOfSizeP creates a new JSON array of a particular size at a path using | ||||
| // dot notation. Returns an error if the path contains a collision with a non | ||||
| // object type. | ||||
| func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) { | ||||
| 	return g.ArrayOfSize(size, strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error | ||||
| // if the object is not an array or the index is out of bounds. | ||||
| // ArrayOfSizeI create a new JSON array of a particular size within an array at | ||||
| // an index. Returns an error if the element is not an array or the index is out | ||||
| // of bounds. | ||||
| func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) { | ||||
| 	a := make([]interface{}, size) | ||||
| 	return g.SetIndex(a, index) | ||||
| } | ||||
|  | ||||
| // Delete - Delete an element at a JSON path, an error is returned if the element does not exist. | ||||
| // Delete an element at a path, an error is returned if the element does not | ||||
| // exist. | ||||
| func (g *Container) Delete(path ...string) error { | ||||
| 	var object interface{} | ||||
|  | ||||
| @@ -304,45 +419,40 @@ func (g *Container) Delete(path ...string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // DeleteP - Does the same as Delete, but using a dot notation JSON path. | ||||
| // DeleteP deletes an element at a path using dot notation, an error is returned | ||||
| // if the element does not exist. | ||||
| func (g *Container) DeleteP(path string) error { | ||||
| 	return g.Delete(strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // Merge - Merges two gabs-containers | ||||
| func (g *Container) Merge(toMerge *Container) error { | ||||
| // MergeFn merges two objects using a provided function to resolve collisions. | ||||
| // | ||||
| // The collision function receives two interface{} arguments, destination (the | ||||
| // original object) and source (the object being merged into the destination). | ||||
| // Which ever value is returned becomes the new value in the destination object | ||||
| // at the location of the collision. | ||||
| func (g *Container) MergeFn(source *Container, collisionFn func(destination, source interface{}) interface{}) error { | ||||
| 	var recursiveFnc func(map[string]interface{}, []string) error | ||||
| 	recursiveFnc = func(mmap map[string]interface{}, path []string) error { | ||||
| 		for key, value := range mmap { | ||||
| 			newPath := append(path, key) | ||||
| 			if g.Exists(newPath...) { | ||||
| 				target := g.Search(newPath...) | ||||
| 				existingData := g.Search(newPath...).Data() | ||||
| 				switch t := value.(type) { | ||||
| 				case map[string]interface{}: | ||||
| 					switch targetV := target.Data().(type) { | ||||
| 					switch existingVal := existingData.(type) { | ||||
| 					case map[string]interface{}: | ||||
| 						if err := recursiveFnc(t, newPath); err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| 					case []interface{}: | ||||
| 						g.Set(append(targetV, t), newPath...) | ||||
| 					default: | ||||
| 						newSlice := append([]interface{}{}, targetV) | ||||
| 						g.Set(append(newSlice, t), newPath...) | ||||
| 					} | ||||
| 				case []interface{}: | ||||
| 					for _, valueOfSlice := range t { | ||||
| 						if err := g.ArrayAppend(valueOfSlice, newPath...); err != nil { | ||||
| 						if _, err := g.Set(collisionFn(existingVal, t), newPath...); err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| 					} | ||||
| 				default: | ||||
| 					switch targetV := target.Data().(type) { | ||||
| 					case []interface{}: | ||||
| 						g.Set(append(targetV, t), newPath...) | ||||
| 					default: | ||||
| 						newSlice := append([]interface{}{}, targetV) | ||||
| 						g.Set(append(newSlice, t), newPath...) | ||||
| 					if _, err := g.Set(collisionFn(existingData, t), newPath...); err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| @@ -354,21 +464,48 @@ func (g *Container) Merge(toMerge *Container) error { | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 	if mmap, ok := toMerge.Data().(map[string]interface{}); ok { | ||||
| 	if mmap, ok := source.Data().(map[string]interface{}); ok { | ||||
| 		return recursiveFnc(mmap, []string{}) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| // Merge a source object into an existing destination object. When a collision | ||||
| // is found within the merged structures (both a source and destination object | ||||
| // contain the same non-object keys) the result will be an array containing both | ||||
| // values, where values that are already arrays will be expanded into the | ||||
| // resulting array. | ||||
| // | ||||
| // It is possible to merge structures will different collision behaviours with | ||||
| // MergeFn. | ||||
| func (g *Container) Merge(source *Container) error { | ||||
| 	return g.MergeFn(source, func(dest, source interface{}) interface{} { | ||||
| 		destArr, destIsArray := dest.([]interface{}) | ||||
| 		sourceArr, sourceIsArray := source.([]interface{}) | ||||
| 		if destIsArray { | ||||
| 			if sourceIsArray { | ||||
| 				return append(destArr, sourceArr...) | ||||
| 			} | ||||
| 			return append(destArr, source) | ||||
| 		} | ||||
| 		if sourceIsArray { | ||||
| 			return append(append([]interface{}{}, dest), sourceArr...) | ||||
| 		} | ||||
| 		return []interface{}{dest, source} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| /* | ||||
| Array modification/search - Keeping these options simple right now, no need for anything more | ||||
| complicated since you can just cast to []interface{}, modify and then reassign with Set. | ||||
| Array modification/search - Keeping these options simple right now, no need for | ||||
| anything more complicated since you can just cast to []interface{}, modify and | ||||
| then reassign with Set. | ||||
| */ | ||||
|  | ||||
| // ArrayAppend - Append a value onto a JSON array. If the target is not a JSON array then it will be | ||||
| // converted into one, with its contents as the first element of the array. | ||||
| // ArrayAppend attempts to append a value onto a JSON array at a path. If the | ||||
| // target is not a JSON array then it will be converted into one, with its | ||||
| // original contents set to the first element of the array. | ||||
| func (g *Container) ArrayAppend(value interface{}, path ...string) error { | ||||
| 	if array, ok := g.Search(path...).Data().([]interface{}); ok { | ||||
| 		array = append(array, value) | ||||
| @@ -386,12 +523,15 @@ func (g *Container) ArrayAppend(value interface{}, path ...string) error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path. | ||||
| // ArrayAppendP attempts to append a value onto a JSON array at a path using dot | ||||
| // notation. If the target is not a JSON array then it will be converted into | ||||
| // one, with its original contents set to the first element of the array. | ||||
| func (g *Container) ArrayAppendP(value interface{}, path string) error { | ||||
| 	return g.ArrayAppend(value, strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // ArrayRemove - Remove an element from a JSON array. | ||||
| // ArrayRemove attempts to remove an element identified by an index from a JSON | ||||
| // array at a path. | ||||
| func (g *Container) ArrayRemove(index int, path ...string) error { | ||||
| 	if index < 0 { | ||||
| 		return ErrOutOfBounds | ||||
| @@ -409,12 +549,14 @@ func (g *Container) ArrayRemove(index int, path ...string) error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path. | ||||
| // ArrayRemoveP attempts to remove an element identified by an index from a JSON | ||||
| // array at a path using dot notation. | ||||
| func (g *Container) ArrayRemoveP(index int, path string) error { | ||||
| 	return g.ArrayRemove(index, strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // ArrayElement - Access an element from a JSON array. | ||||
| // ArrayElement attempts to access an element by an index from a JSON array at a | ||||
| // path. | ||||
| func (g *Container) ArrayElement(index int, path ...string) (*Container, error) { | ||||
| 	if index < 0 { | ||||
| 		return &Container{nil}, ErrOutOfBounds | ||||
| @@ -429,12 +571,13 @@ func (g *Container) ArrayElement(index int, path ...string) (*Container, error) | ||||
| 	return &Container{nil}, ErrOutOfBounds | ||||
| } | ||||
|  | ||||
| // ArrayElementP - Access an element from a JSON array using a dot notation JSON path. | ||||
| // ArrayElementP attempts to access an element by an index from a JSON array at | ||||
| // a path using dot notation. | ||||
| func (g *Container) ArrayElementP(index int, path string) (*Container, error) { | ||||
| 	return g.ArrayElement(index, strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| // ArrayCount - Count the number of elements in a JSON array. | ||||
| // ArrayCount counts the number of elements in a JSON array at a path. | ||||
| func (g *Container) ArrayCount(path ...string) (int, error) { | ||||
| 	if array, ok := g.Search(path...).Data().([]interface{}); ok { | ||||
| 		return len(array), nil | ||||
| @@ -442,14 +585,15 @@ func (g *Container) ArrayCount(path ...string) (int, error) { | ||||
| 	return 0, ErrNotArray | ||||
| } | ||||
|  | ||||
| // ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path. | ||||
| // ArrayCountP counts the number of elements in a JSON array at a path using dot | ||||
| // notation. | ||||
| func (g *Container) ArrayCountP(path string) (int, error) { | ||||
| 	return g.ArrayCount(strings.Split(path, ".")...) | ||||
| } | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| // Bytes - Converts the contained object back to a JSON []byte blob. | ||||
| // Bytes marshals an element to a JSON []byte blob. | ||||
| func (g *Container) Bytes() []byte { | ||||
| 	if g.Data() != nil { | ||||
| 		if bytes, err := json.Marshal(g.object); err == nil { | ||||
| @@ -459,7 +603,8 @@ func (g *Container) Bytes() []byte { | ||||
| 	return []byte("{}") | ||||
| } | ||||
|  | ||||
| // BytesIndent - Converts the contained object to a JSON []byte blob formatted with prefix, indent. | ||||
| // BytesIndent marshals an element to a JSON []byte blob formatted with a prefix | ||||
| // and indent string. | ||||
| func (g *Container) BytesIndent(prefix string, indent string) []byte { | ||||
| 	if g.object != nil { | ||||
| 		if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil { | ||||
| @@ -469,12 +614,13 @@ func (g *Container) BytesIndent(prefix string, indent string) []byte { | ||||
| 	return []byte("{}") | ||||
| } | ||||
|  | ||||
| // String - Converts the contained object to a JSON formatted string. | ||||
| // String marshals an element to a JSON formatted string. | ||||
| func (g *Container) String() string { | ||||
| 	return string(g.Bytes()) | ||||
| } | ||||
|  | ||||
| // StringIndent - Converts the contained object back to a JSON formatted string with prefix, indent. | ||||
| // StringIndent marshals an element to a JSON string formatted with a prefix and | ||||
| // indent string. | ||||
| func (g *Container) StringIndent(prefix string, indent string) string { | ||||
| 	return string(g.BytesIndent(prefix, indent)) | ||||
| } | ||||
| @@ -496,10 +642,9 @@ func EncodeOptIndent(prefix string, indent string) EncodeOpt { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // EncodeJSON - Encodes the contained object back to a JSON formatted []byte | ||||
| // using a variant list of modifier functions for the encoder being used. | ||||
| // Functions for modifying the output are prefixed with EncodeOpt, e.g. | ||||
| // EncodeOptHTMLEscape. | ||||
| // EncodeJSON marshals an element to a JSON formatted []byte using a variant | ||||
| // list of modifier functions for the encoder being used. Functions for | ||||
| // modifying the output are prefixed with EncodeOpt, e.g. EncodeOptHTMLEscape. | ||||
| func (g *Container) EncodeJSON(encodeOpts ...EncodeOpt) []byte { | ||||
| 	var b bytes.Buffer | ||||
| 	encoder := json.NewEncoder(&b) | ||||
| @@ -517,17 +662,18 @@ func (g *Container) EncodeJSON(encodeOpts ...EncodeOpt) []byte { | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // New - Create a new gabs JSON object. | ||||
| // New creates a new gabs JSON object. | ||||
| func New() *Container { | ||||
| 	return &Container{map[string]interface{}{}} | ||||
| } | ||||
|  | ||||
| // Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object. | ||||
| // Consume an already unmarshalled JSON object (or a new map[string]interface{}) | ||||
| // into a *Container. | ||||
| func Consume(root interface{}) (*Container, error) { | ||||
| 	return &Container{root}, nil | ||||
| } | ||||
|  | ||||
| // ParseJSON - Convert a string into a representation of the parsed JSON. | ||||
| // ParseJSON unmarshals a JSON byte slice into a *Container. | ||||
| func ParseJSON(sample []byte) (*Container, error) { | ||||
| 	var gabs Container | ||||
|  | ||||
| @@ -538,7 +684,7 @@ func ParseJSON(sample []byte) (*Container, error) { | ||||
| 	return &gabs, nil | ||||
| } | ||||
|  | ||||
| // ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON. | ||||
| // ParseJSONDecoder applies a json.Decoder to a *Container. | ||||
| func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) { | ||||
| 	var gabs Container | ||||
|  | ||||
| @@ -549,7 +695,7 @@ func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) { | ||||
| 	return &gabs, nil | ||||
| } | ||||
|  | ||||
| // ParseJSONFile - Read a file and convert into a representation of the parsed JSON. | ||||
| // ParseJSONFile reads a file and unmarshals the contents into a *Container. | ||||
| func ParseJSONFile(path string) (*Container, error) { | ||||
| 	if len(path) > 0 { | ||||
| 		cBytes, err := ioutil.ReadFile(path) | ||||
| @@ -567,7 +713,7 @@ func ParseJSONFile(path string) (*Container, error) { | ||||
| 	return nil, ErrInvalidPath | ||||
| } | ||||
|  | ||||
| // ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON. | ||||
| // ParseJSONBuffer reads a buffer and unmarshals the contents into a *Container. | ||||
| func ParseJSONBuffer(buffer io.Reader) (*Container, error) { | ||||
| 	var gabs Container | ||||
| 	jsonDecoder := json.NewDecoder(buffer) | ||||
| @@ -578,4 +724,4 @@ func ParseJSONBuffer(buffer io.Reader) (*Container, error) { | ||||
| 	return &gabs, nil | ||||
| } | ||||
|  | ||||
| //-------------------------------------------------------------------------------------------------- | ||||
| //------------------------------------------------------------------------------ | ||||
|   | ||||
							
								
								
									
										1
									
								
								vendor/github.com/Jeffail/gabs/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/Jeffail/gabs/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| module github.com/Jeffail/gabs | ||||
							
								
								
									
										5
									
								
								vendor/github.com/Philipp15b/go-steam/.gitmodules
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/Philipp15b/go-steam/.gitmodules
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,3 +1,6 @@ | ||||
| [submodule "generator/SteamKit"] | ||||
| 	path = generator/SteamKit | ||||
| 	url = https://github.com/Philipp15b/SteamKit.git | ||||
| 	url = https://github.com/SteamRE/SteamKit.git | ||||
| [submodule "generator/Protobufs"] | ||||
| 	path = generator/Protobufs | ||||
| 	url = https://github.com/SteamDatabase/Protobufs.git | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/Philipp15b/go-steam/auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/Philipp15b/go-steam/auth.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -119,7 +119,7 @@ func (a *Auth) handleLogOnResponse(packet *Packet) { | ||||
| 			ExtendedResult:            EResult(body.GetEresultExtended()), | ||||
| 			OutOfGameSecsPerHeartbeat: body.GetOutOfGameHeartbeatSeconds(), | ||||
| 			InGameSecsPerHeartbeat:    body.GetInGameHeartbeatSeconds(), | ||||
| 			PublicIp:                  body.GetPublicIp(), | ||||
| 			PublicIp:                  body.GetDeprecatedPublicIp(), | ||||
| 			ServerTime:                body.GetRtime32ServerTime(), | ||||
| 			AccountFlags:              EAccountFlags(body.GetAccountFlags()), | ||||
| 			ClientSteamId:             SteamId(body.GetClientSuppliedSteamid()), | ||||
| @@ -127,7 +127,7 @@ func (a *Auth) handleLogOnResponse(packet *Packet) { | ||||
| 			CellId:                    body.GetCellId(), | ||||
| 			CellIdPingThreshold:       body.GetCellIdPingThreshold(), | ||||
| 			Steam2Ticket:              body.GetSteam2Ticket(), | ||||
| 			UsePics:                   body.GetUsePics(), | ||||
| 			UsePics:                   body.GetDeprecatedUsePics(), | ||||
| 			WebApiUserNonce:           body.GetWebapiAuthenticateUserNonce(), | ||||
| 			IpCountryCode:             body.GetIpCountryCode(), | ||||
| 			VanityUrl:                 body.GetVanityUrl(), | ||||
|   | ||||
							
								
								
									
										8
									
								
								vendor/github.com/Philipp15b/go-steam/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/Philipp15b/go-steam/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| module github.com/Philipp15b/go-steam | ||||
|  | ||||
| go 1.14 | ||||
|  | ||||
| require ( | ||||
| 	github.com/davecgh/go-spew v1.1.1 | ||||
| 	github.com/golang/protobuf v1.4.2 | ||||
| ) | ||||
							
								
								
									
										22
									
								
								vendor/github.com/Philipp15b/go-steam/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/Philipp15b/go-steam/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| 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/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= | ||||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
							
								
								
									
										32
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/app_ticket.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/app_ticket.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -21,11 +21,11 @@ var _ = math.Inf | ||||
| const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||
|  | ||||
| type EncryptedAppTicket struct { | ||||
| 	TicketVersionNo               *uint32  `protobuf:"varint,1,opt,name=ticket_version_no" json:"ticket_version_no,omitempty"` | ||||
| 	CrcEncryptedticket            *uint32  `protobuf:"varint,2,opt,name=crc_encryptedticket" json:"crc_encryptedticket,omitempty"` | ||||
| 	CbEncrypteduserdata           *uint32  `protobuf:"varint,3,opt,name=cb_encrypteduserdata" json:"cb_encrypteduserdata,omitempty"` | ||||
| 	CbEncryptedAppownershipticket *uint32  `protobuf:"varint,4,opt,name=cb_encrypted_appownershipticket" json:"cb_encrypted_appownershipticket,omitempty"` | ||||
| 	EncryptedTicket               []byte   `protobuf:"bytes,5,opt,name=encrypted_ticket" json:"encrypted_ticket,omitempty"` | ||||
| 	TicketVersionNo               *uint32  `protobuf:"varint,1,opt,name=ticket_version_no,json=ticketVersionNo" json:"ticket_version_no,omitempty"` | ||||
| 	CrcEncryptedticket            *uint32  `protobuf:"varint,2,opt,name=crc_encryptedticket,json=crcEncryptedticket" json:"crc_encryptedticket,omitempty"` | ||||
| 	CbEncrypteduserdata           *uint32  `protobuf:"varint,3,opt,name=cb_encrypteduserdata,json=cbEncrypteduserdata" json:"cb_encrypteduserdata,omitempty"` | ||||
| 	CbEncryptedAppownershipticket *uint32  `protobuf:"varint,4,opt,name=cb_encrypted_appownershipticket,json=cbEncryptedAppownershipticket" json:"cb_encrypted_appownershipticket,omitempty"` | ||||
| 	EncryptedTicket               []byte   `protobuf:"bytes,5,opt,name=encrypted_ticket,json=encryptedTicket" json:"encrypted_ticket,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral          struct{} `json:"-"` | ||||
| 	XXX_unrecognized              []byte   `json:"-"` | ||||
| 	XXX_sizecache                 int32    `json:"-"` | ||||
| @@ -98,16 +98,18 @@ func init() { | ||||
| func init() { proto.RegisterFile("encrypted_app_ticket.proto", fileDescriptor_c6d69fd1cac4e8d5) } | ||||
|  | ||||
| var fileDescriptor_c6d69fd1cac4e8d5 = []byte{ | ||||
| 	// 164 bytes of a gzipped FileDescriptorProto | ||||
| 	// 202 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcd, 0x4b, 0x2e, | ||||
| 	0xaa, 0x2c, 0x28, 0x49, 0x4d, 0x89, 0x4f, 0x2c, 0x28, 0x88, 0x2f, 0xc9, 0x4c, 0xce, 0x4e, 0x2d, | ||||
| 	0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x5a, 0xcb, 0xc8, 0x25, 0xe4, 0x0a, 0x93, 0x76, 0x2c, | ||||
| 	0x28, 0x08, 0x01, 0x4b, 0x0a, 0x49, 0x72, 0x09, 0x42, 0x94, 0xc5, 0x97, 0xa5, 0x16, 0x15, 0x67, | ||||
| 	0xe6, 0xe7, 0xc5, 0xe7, 0xe5, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x0a, 0x49, 0x73, 0x09, 0x27, | ||||
| 	0x17, 0x25, 0xc7, 0xc3, 0xcd, 0x84, 0xa8, 0x93, 0x60, 0x02, 0x4b, 0xca, 0x70, 0x89, 0x24, 0x27, | ||||
| 	0x21, 0xe4, 0x4a, 0x8b, 0x53, 0x8b, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1, 0xb2, 0xea, 0x5c, | ||||
| 	0xf2, 0xc8, 0xb2, 0x20, 0xd7, 0xe4, 0x97, 0xe7, 0xa5, 0x16, 0x15, 0x67, 0x64, 0x16, 0x40, 0x8d, | ||||
| 	0x61, 0x01, 0x2b, 0x94, 0xe0, 0x12, 0x40, 0xa8, 0x82, 0xca, 0xb0, 0x2a, 0x30, 0x6a, 0xf0, 0x38, | ||||
| 	0xb1, 0x7a, 0x30, 0x36, 0x30, 0x32, 0x00, 0x02, 0x00, 0x00, 0xff, 0xff, 0x03, 0x8c, 0xdb, 0x92, | ||||
| 	0xd3, 0x00, 0x00, 0x00, | ||||
| 	0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x9a, 0xce, 0xc4, 0x25, 0xe4, 0x0a, 0x93, 0x76, 0x2c, | ||||
| 	0x28, 0x08, 0x01, 0x4b, 0x0a, 0x69, 0x71, 0x09, 0x42, 0x94, 0xc5, 0x97, 0xa5, 0x16, 0x15, 0x67, | ||||
| 	0xe6, 0xe7, 0xc5, 0xe7, 0xe5, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x06, 0xf1, 0x43, 0x24, 0xc2, | ||||
| 	0x20, 0xe2, 0x7e, 0xf9, 0x42, 0xfa, 0x5c, 0xc2, 0xc9, 0x45, 0xc9, 0xf1, 0x70, 0x4b, 0x20, 0xf2, | ||||
| 	0x12, 0x4c, 0x60, 0xd5, 0x42, 0xc9, 0x45, 0xc9, 0xae, 0xa8, 0x32, 0x42, 0x86, 0x5c, 0x22, 0xc9, | ||||
| 	0x49, 0x08, 0xf5, 0xa5, 0xc5, 0xa9, 0x45, 0x29, 0x89, 0x25, 0x89, 0x12, 0xcc, 0x60, 0x1d, 0xc2, | ||||
| 	0xc9, 0x49, 0xae, 0xe8, 0x52, 0x42, 0x6e, 0x5c, 0xf2, 0xc8, 0x5a, 0x40, 0xfe, 0xc8, 0x2f, 0xcf, | ||||
| 	0x4b, 0x2d, 0x2a, 0xce, 0xc8, 0x2c, 0x80, 0xda, 0xc7, 0x02, 0xd6, 0x2d, 0x8b, 0xa4, 0xdb, 0x11, | ||||
| 	0x43, 0x91, 0x90, 0x26, 0x97, 0x00, 0xc2, 0x10, 0xa8, 0x46, 0x56, 0x05, 0x46, 0x0d, 0x9e, 0x20, | ||||
| 	0x7e, 0xb8, 0x38, 0x24, 0x08, 0x9c, 0x58, 0x3d, 0x18, 0x1b, 0x18, 0x19, 0x00, 0x01, 0x00, 0x00, | ||||
| 	0xff, 0xff, 0xe8, 0x03, 0x98, 0x21, 0x3d, 0x01, 0x00, 0x00, | ||||
| } | ||||
|   | ||||
							
								
								
									
										1221
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/base.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1221
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/base.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2801
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2801
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2860
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_2.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2860
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_2.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										531
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_friends.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										531
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_friends.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -22,10 +22,10 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||
|  | ||||
| type CMsgClientFriendMsg struct { | ||||
| 	Steamid                *uint64  `protobuf:"fixed64,1,opt,name=steamid" json:"steamid,omitempty"` | ||||
| 	ChatEntryType          *int32   `protobuf:"varint,2,opt,name=chat_entry_type" json:"chat_entry_type,omitempty"` | ||||
| 	ChatEntryType          *int32   `protobuf:"varint,2,opt,name=chat_entry_type,json=chatEntryType" json:"chat_entry_type,omitempty"` | ||||
| 	Message                []byte   `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"` | ||||
| 	Rtime32ServerTimestamp *uint32  `protobuf:"fixed32,4,opt,name=rtime32_server_timestamp" json:"rtime32_server_timestamp,omitempty"` | ||||
| 	EchoToSender           *bool    `protobuf:"varint,5,opt,name=echo_to_sender" json:"echo_to_sender,omitempty"` | ||||
| 	Rtime32ServerTimestamp *uint32  `protobuf:"fixed32,4,opt,name=rtime32_server_timestamp,json=rtime32ServerTimestamp" json:"rtime32_server_timestamp,omitempty"` | ||||
| 	EchoToSender           *bool    `protobuf:"varint,5,opt,name=echo_to_sender,json=echoToSender" json:"echo_to_sender,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral   struct{} `json:"-"` | ||||
| 	XXX_unrecognized       []byte   `json:"-"` | ||||
| 	XXX_sizecache          int32    `json:"-"` | ||||
| @@ -92,11 +92,11 @@ func (m *CMsgClientFriendMsg) GetEchoToSender() bool { | ||||
| } | ||||
|  | ||||
| type CMsgClientFriendMsgIncoming struct { | ||||
| 	SteamidFrom            *uint64  `protobuf:"fixed64,1,opt,name=steamid_from" json:"steamid_from,omitempty"` | ||||
| 	ChatEntryType          *int32   `protobuf:"varint,2,opt,name=chat_entry_type" json:"chat_entry_type,omitempty"` | ||||
| 	FromLimitedAccount     *bool    `protobuf:"varint,3,opt,name=from_limited_account" json:"from_limited_account,omitempty"` | ||||
| 	SteamidFrom            *uint64  `protobuf:"fixed64,1,opt,name=steamid_from,json=steamidFrom" json:"steamid_from,omitempty"` | ||||
| 	ChatEntryType          *int32   `protobuf:"varint,2,opt,name=chat_entry_type,json=chatEntryType" json:"chat_entry_type,omitempty"` | ||||
| 	FromLimitedAccount     *bool    `protobuf:"varint,3,opt,name=from_limited_account,json=fromLimitedAccount" json:"from_limited_account,omitempty"` | ||||
| 	Message                []byte   `protobuf:"bytes,4,opt,name=message" json:"message,omitempty"` | ||||
| 	Rtime32ServerTimestamp *uint32  `protobuf:"fixed32,5,opt,name=rtime32_server_timestamp" json:"rtime32_server_timestamp,omitempty"` | ||||
| 	Rtime32ServerTimestamp *uint32  `protobuf:"fixed32,5,opt,name=rtime32_server_timestamp,json=rtime32ServerTimestamp" json:"rtime32_server_timestamp,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral   struct{} `json:"-"` | ||||
| 	XXX_unrecognized       []byte   `json:"-"` | ||||
| 	XXX_sizecache          int32    `json:"-"` | ||||
| @@ -163,8 +163,8 @@ func (m *CMsgClientFriendMsgIncoming) GetRtime32ServerTimestamp() uint32 { | ||||
| } | ||||
|  | ||||
| type CMsgClientAddFriend struct { | ||||
| 	SteamidToAdd            *uint64  `protobuf:"fixed64,1,opt,name=steamid_to_add" json:"steamid_to_add,omitempty"` | ||||
| 	AccountnameOrEmailToAdd *string  `protobuf:"bytes,2,opt,name=accountname_or_email_to_add" json:"accountname_or_email_to_add,omitempty"` | ||||
| 	SteamidToAdd            *uint64  `protobuf:"fixed64,1,opt,name=steamid_to_add,json=steamidToAdd" json:"steamid_to_add,omitempty"` | ||||
| 	AccountnameOrEmailToAdd *string  `protobuf:"bytes,2,opt,name=accountname_or_email_to_add,json=accountnameOrEmailToAdd" json:"accountname_or_email_to_add,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral    struct{} `json:"-"` | ||||
| 	XXX_unrecognized        []byte   `json:"-"` | ||||
| 	XXX_sizecache           int32    `json:"-"` | ||||
| @@ -211,8 +211,8 @@ func (m *CMsgClientAddFriend) GetAccountnameOrEmailToAdd() string { | ||||
|  | ||||
| type CMsgClientAddFriendResponse struct { | ||||
| 	Eresult              *int32   `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||
| 	SteamIdAdded         *uint64  `protobuf:"fixed64,2,opt,name=steam_id_added" json:"steam_id_added,omitempty"` | ||||
| 	PersonaNameAdded     *string  `protobuf:"bytes,3,opt,name=persona_name_added" json:"persona_name_added,omitempty"` | ||||
| 	SteamIdAdded         *uint64  `protobuf:"fixed64,2,opt,name=steam_id_added,json=steamIdAdded" json:"steam_id_added,omitempty"` | ||||
| 	PersonaNameAdded     *string  `protobuf:"bytes,3,opt,name=persona_name_added,json=personaNameAdded" json:"persona_name_added,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -355,9 +355,9 @@ func (m *CMsgClientHideFriend) GetHide() bool { | ||||
| type CMsgClientFriendsList struct { | ||||
| 	Bincremental         *bool                           `protobuf:"varint,1,opt,name=bincremental" json:"bincremental,omitempty"` | ||||
| 	Friends              []*CMsgClientFriendsList_Friend `protobuf:"bytes,2,rep,name=friends" json:"friends,omitempty"` | ||||
| 	MaxFriendCount       *uint32                         `protobuf:"varint,3,opt,name=max_friend_count" json:"max_friend_count,omitempty"` | ||||
| 	ActiveFriendCount    *uint32                         `protobuf:"varint,4,opt,name=active_friend_count" json:"active_friend_count,omitempty"` | ||||
| 	FriendsLimitHit      *bool                           `protobuf:"varint,5,opt,name=friends_limit_hit" json:"friends_limit_hit,omitempty"` | ||||
| 	MaxFriendCount       *uint32                         `protobuf:"varint,3,opt,name=max_friend_count,json=maxFriendCount" json:"max_friend_count,omitempty"` | ||||
| 	ActiveFriendCount    *uint32                         `protobuf:"varint,4,opt,name=active_friend_count,json=activeFriendCount" json:"active_friend_count,omitempty"` | ||||
| 	FriendsLimitHit      *bool                           `protobuf:"varint,5,opt,name=friends_limit_hit,json=friendsLimitHit" json:"friends_limit_hit,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}                        `json:"-"` | ||||
| 	XXX_unrecognized     []byte                          `json:"-"` | ||||
| 	XXX_sizecache        int32                           `json:"-"` | ||||
| @@ -826,7 +826,7 @@ func (m *CMsgClientSetPlayerNicknameResponse) GetEresult() uint32 { | ||||
| } | ||||
|  | ||||
| type CMsgClientRequestFriendData struct { | ||||
| 	PersonaStateRequested *uint32  `protobuf:"varint,1,opt,name=persona_state_requested" json:"persona_state_requested,omitempty"` | ||||
| 	PersonaStateRequested *uint32  `protobuf:"varint,1,opt,name=persona_state_requested,json=personaStateRequested" json:"persona_state_requested,omitempty"` | ||||
| 	Friends               []uint64 `protobuf:"fixed64,2,rep,name=friends" json:"friends,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral  struct{} `json:"-"` | ||||
| 	XXX_unrecognized      []byte   `json:"-"` | ||||
| @@ -873,14 +873,14 @@ func (m *CMsgClientRequestFriendData) GetFriends() []uint64 { | ||||
| } | ||||
|  | ||||
| type CMsgClientChangeStatus struct { | ||||
| 	PersonaState         *uint32  `protobuf:"varint,1,opt,name=persona_state" json:"persona_state,omitempty"` | ||||
| 	PlayerName           *string  `protobuf:"bytes,2,opt,name=player_name" json:"player_name,omitempty"` | ||||
| 	IsAutoGeneratedName  *bool    `protobuf:"varint,3,opt,name=is_auto_generated_name" json:"is_auto_generated_name,omitempty"` | ||||
| 	HighPriority         *bool    `protobuf:"varint,4,opt,name=high_priority" json:"high_priority,omitempty"` | ||||
| 	PersonaSetByUser     *bool    `protobuf:"varint,5,opt,name=persona_set_by_user" json:"persona_set_by_user,omitempty"` | ||||
| 	PersonaStateFlags    *uint32  `protobuf:"varint,6,opt,name=persona_state_flags,def=0" json:"persona_state_flags,omitempty"` | ||||
| 	NeedPersonaResponse  *bool    `protobuf:"varint,7,opt,name=need_persona_response" json:"need_persona_response,omitempty"` | ||||
| 	IsClientIdle         *bool    `protobuf:"varint,8,opt,name=is_client_idle" json:"is_client_idle,omitempty"` | ||||
| 	PersonaState         *uint32  `protobuf:"varint,1,opt,name=persona_state,json=personaState" json:"persona_state,omitempty"` | ||||
| 	PlayerName           *string  `protobuf:"bytes,2,opt,name=player_name,json=playerName" json:"player_name,omitempty"` | ||||
| 	IsAutoGeneratedName  *bool    `protobuf:"varint,3,opt,name=is_auto_generated_name,json=isAutoGeneratedName" json:"is_auto_generated_name,omitempty"` | ||||
| 	HighPriority         *bool    `protobuf:"varint,4,opt,name=high_priority,json=highPriority" json:"high_priority,omitempty"` | ||||
| 	PersonaSetByUser     *bool    `protobuf:"varint,5,opt,name=persona_set_by_user,json=personaSetByUser" json:"persona_set_by_user,omitempty"` | ||||
| 	PersonaStateFlags    *uint32  `protobuf:"varint,6,opt,name=persona_state_flags,json=personaStateFlags,def=0" json:"persona_state_flags,omitempty"` | ||||
| 	NeedPersonaResponse  *bool    `protobuf:"varint,7,opt,name=need_persona_response,json=needPersonaResponse" json:"need_persona_response,omitempty"` | ||||
| 	IsClientIdle         *bool    `protobuf:"varint,8,opt,name=is_client_idle,json=isClientIdle" json:"is_client_idle,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -971,7 +971,7 @@ func (m *CMsgClientChangeStatus) GetIsClientIdle() bool { | ||||
|  | ||||
| type CMsgPersonaChangeResponse struct { | ||||
| 	Result               *uint32  `protobuf:"varint,1,opt,name=result" json:"result,omitempty"` | ||||
| 	PlayerName           *string  `protobuf:"bytes,2,opt,name=player_name" json:"player_name,omitempty"` | ||||
| 	PlayerName           *string  `protobuf:"bytes,2,opt,name=player_name,json=playerName" json:"player_name,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -1017,7 +1017,7 @@ func (m *CMsgPersonaChangeResponse) GetPlayerName() string { | ||||
| } | ||||
|  | ||||
| type CMsgClientPersonaState struct { | ||||
| 	StatusFlags          *uint32                          `protobuf:"varint,1,opt,name=status_flags" json:"status_flags,omitempty"` | ||||
| 	StatusFlags          *uint32                          `protobuf:"varint,1,opt,name=status_flags,json=statusFlags" json:"status_flags,omitempty"` | ||||
| 	Friends              []*CMsgClientPersonaState_Friend `protobuf:"bytes,2,rep,name=friends" json:"friends,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}                         `json:"-"` | ||||
| 	XXX_unrecognized     []byte                           `json:"-"` | ||||
| @@ -1065,34 +1065,33 @@ func (m *CMsgClientPersonaState) GetFriends() []*CMsgClientPersonaState_Friend { | ||||
|  | ||||
| type CMsgClientPersonaState_Friend struct { | ||||
| 	Friendid                   *uint64                                 `protobuf:"fixed64,1,opt,name=friendid" json:"friendid,omitempty"` | ||||
| 	PersonaState               *uint32                                 `protobuf:"varint,2,opt,name=persona_state" json:"persona_state,omitempty"` | ||||
| 	GamePlayedAppId            *uint32                                 `protobuf:"varint,3,opt,name=game_played_app_id" json:"game_played_app_id,omitempty"` | ||||
| 	GameServerIp               *uint32                                 `protobuf:"varint,4,opt,name=game_server_ip" json:"game_server_ip,omitempty"` | ||||
| 	GameServerPort             *uint32                                 `protobuf:"varint,5,opt,name=game_server_port" json:"game_server_port,omitempty"` | ||||
| 	PersonaStateFlags          *uint32                                 `protobuf:"varint,6,opt,name=persona_state_flags" json:"persona_state_flags,omitempty"` | ||||
| 	OnlineSessionInstances     *uint32                                 `protobuf:"varint,7,opt,name=online_session_instances" json:"online_session_instances,omitempty"` | ||||
| 	PublishedInstanceId        *uint32                                 `protobuf:"varint,8,opt,name=published_instance_id" json:"published_instance_id,omitempty"` | ||||
| 	PersonaSetByUser           *bool                                   `protobuf:"varint,10,opt,name=persona_set_by_user" json:"persona_set_by_user,omitempty"` | ||||
| 	PlayerName                 *string                                 `protobuf:"bytes,15,opt,name=player_name" json:"player_name,omitempty"` | ||||
| 	QueryPort                  *uint32                                 `protobuf:"varint,20,opt,name=query_port" json:"query_port,omitempty"` | ||||
| 	SteamidSource              *uint64                                 `protobuf:"fixed64,25,opt,name=steamid_source" json:"steamid_source,omitempty"` | ||||
| 	AvatarHash                 []byte                                  `protobuf:"bytes,31,opt,name=avatar_hash" json:"avatar_hash,omitempty"` | ||||
| 	LastLogoff                 *uint32                                 `protobuf:"varint,45,opt,name=last_logoff" json:"last_logoff,omitempty"` | ||||
| 	LastLogon                  *uint32                                 `protobuf:"varint,46,opt,name=last_logon" json:"last_logon,omitempty"` | ||||
| 	LastSeenOnline             *uint32                                 `protobuf:"varint,47,opt,name=last_seen_online" json:"last_seen_online,omitempty"` | ||||
| 	ClanRank                   *uint32                                 `protobuf:"varint,50,opt,name=clan_rank" json:"clan_rank,omitempty"` | ||||
| 	GameName                   *string                                 `protobuf:"bytes,55,opt,name=game_name" json:"game_name,omitempty"` | ||||
| 	PersonaState               *uint32                                 `protobuf:"varint,2,opt,name=persona_state,json=personaState" json:"persona_state,omitempty"` | ||||
| 	GamePlayedAppId            *uint32                                 `protobuf:"varint,3,opt,name=game_played_app_id,json=gamePlayedAppId" json:"game_played_app_id,omitempty"` | ||||
| 	GameServerIp               *uint32                                 `protobuf:"varint,4,opt,name=game_server_ip,json=gameServerIp" json:"game_server_ip,omitempty"` | ||||
| 	GameServerPort             *uint32                                 `protobuf:"varint,5,opt,name=game_server_port,json=gameServerPort" json:"game_server_port,omitempty"` | ||||
| 	PersonaStateFlags          *uint32                                 `protobuf:"varint,6,opt,name=persona_state_flags,json=personaStateFlags" json:"persona_state_flags,omitempty"` | ||||
| 	OnlineSessionInstances     *uint32                                 `protobuf:"varint,7,opt,name=online_session_instances,json=onlineSessionInstances" json:"online_session_instances,omitempty"` | ||||
| 	PersonaSetByUser           *bool                                   `protobuf:"varint,10,opt,name=persona_set_by_user,json=personaSetByUser" json:"persona_set_by_user,omitempty"` | ||||
| 	PlayerName                 *string                                 `protobuf:"bytes,15,opt,name=player_name,json=playerName" json:"player_name,omitempty"` | ||||
| 	QueryPort                  *uint32                                 `protobuf:"varint,20,opt,name=query_port,json=queryPort" json:"query_port,omitempty"` | ||||
| 	SteamidSource              *uint64                                 `protobuf:"fixed64,25,opt,name=steamid_source,json=steamidSource" json:"steamid_source,omitempty"` | ||||
| 	AvatarHash                 []byte                                  `protobuf:"bytes,31,opt,name=avatar_hash,json=avatarHash" json:"avatar_hash,omitempty"` | ||||
| 	LastLogoff                 *uint32                                 `protobuf:"varint,45,opt,name=last_logoff,json=lastLogoff" json:"last_logoff,omitempty"` | ||||
| 	LastLogon                  *uint32                                 `protobuf:"varint,46,opt,name=last_logon,json=lastLogon" json:"last_logon,omitempty"` | ||||
| 	LastSeenOnline             *uint32                                 `protobuf:"varint,47,opt,name=last_seen_online,json=lastSeenOnline" json:"last_seen_online,omitempty"` | ||||
| 	ClanRank                   *uint32                                 `protobuf:"varint,50,opt,name=clan_rank,json=clanRank" json:"clan_rank,omitempty"` | ||||
| 	GameName                   *string                                 `protobuf:"bytes,55,opt,name=game_name,json=gameName" json:"game_name,omitempty"` | ||||
| 	Gameid                     *uint64                                 `protobuf:"fixed64,56,opt,name=gameid" json:"gameid,omitempty"` | ||||
| 	GameDataBlob               []byte                                  `protobuf:"bytes,60,opt,name=game_data_blob" json:"game_data_blob,omitempty"` | ||||
| 	ClanData                   *CMsgClientPersonaState_Friend_ClanData `protobuf:"bytes,64,opt,name=clan_data" json:"clan_data,omitempty"` | ||||
| 	ClanTag                    *string                                 `protobuf:"bytes,65,opt,name=clan_tag" json:"clan_tag,omitempty"` | ||||
| 	RichPresence               []*CMsgClientPersonaState_Friend_KV     `protobuf:"bytes,71,rep,name=rich_presence" json:"rich_presence,omitempty"` | ||||
| 	BroadcastId                *uint64                                 `protobuf:"fixed64,72,opt,name=broadcast_id" json:"broadcast_id,omitempty"` | ||||
| 	GameLobbyId                *uint64                                 `protobuf:"fixed64,73,opt,name=game_lobby_id" json:"game_lobby_id,omitempty"` | ||||
| 	WatchingBroadcastAccountid *uint32                                 `protobuf:"varint,74,opt,name=watching_broadcast_accountid" json:"watching_broadcast_accountid,omitempty"` | ||||
| 	WatchingBroadcastAppid     *uint32                                 `protobuf:"varint,75,opt,name=watching_broadcast_appid" json:"watching_broadcast_appid,omitempty"` | ||||
| 	WatchingBroadcastViewers   *uint32                                 `protobuf:"varint,76,opt,name=watching_broadcast_viewers" json:"watching_broadcast_viewers,omitempty"` | ||||
| 	WatchingBroadcastTitle     *string                                 `protobuf:"bytes,77,opt,name=watching_broadcast_title" json:"watching_broadcast_title,omitempty"` | ||||
| 	GameDataBlob               []byte                                  `protobuf:"bytes,60,opt,name=game_data_blob,json=gameDataBlob" json:"game_data_blob,omitempty"` | ||||
| 	ClanData                   *CMsgClientPersonaState_Friend_ClanData `protobuf:"bytes,64,opt,name=clan_data,json=clanData" json:"clan_data,omitempty"` | ||||
| 	ClanTag                    *string                                 `protobuf:"bytes,65,opt,name=clan_tag,json=clanTag" json:"clan_tag,omitempty"` | ||||
| 	RichPresence               []*CMsgClientPersonaState_Friend_KV     `protobuf:"bytes,71,rep,name=rich_presence,json=richPresence" json:"rich_presence,omitempty"` | ||||
| 	BroadcastId                *uint64                                 `protobuf:"fixed64,72,opt,name=broadcast_id,json=broadcastId" json:"broadcast_id,omitempty"` | ||||
| 	GameLobbyId                *uint64                                 `protobuf:"fixed64,73,opt,name=game_lobby_id,json=gameLobbyId" json:"game_lobby_id,omitempty"` | ||||
| 	WatchingBroadcastAccountid *uint32                                 `protobuf:"varint,74,opt,name=watching_broadcast_accountid,json=watchingBroadcastAccountid" json:"watching_broadcast_accountid,omitempty"` | ||||
| 	WatchingBroadcastAppid     *uint32                                 `protobuf:"varint,75,opt,name=watching_broadcast_appid,json=watchingBroadcastAppid" json:"watching_broadcast_appid,omitempty"` | ||||
| 	WatchingBroadcastViewers   *uint32                                 `protobuf:"varint,76,opt,name=watching_broadcast_viewers,json=watchingBroadcastViewers" json:"watching_broadcast_viewers,omitempty"` | ||||
| 	WatchingBroadcastTitle     *string                                 `protobuf:"bytes,77,opt,name=watching_broadcast_title,json=watchingBroadcastTitle" json:"watching_broadcast_title,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral       struct{}                                `json:"-"` | ||||
| 	XXX_unrecognized           []byte                                  `json:"-"` | ||||
| 	XXX_sizecache              int32                                   `json:"-"` | ||||
| @@ -1172,13 +1171,6 @@ func (m *CMsgClientPersonaState_Friend) GetOnlineSessionInstances() uint32 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientPersonaState_Friend) GetPublishedInstanceId() uint32 { | ||||
| 	if m != nil && m.PublishedInstanceId != nil { | ||||
| 		return *m.PublishedInstanceId | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientPersonaState_Friend) GetPersonaSetByUser() bool { | ||||
| 	if m != nil && m.PersonaSetByUser != nil { | ||||
| 		return *m.PersonaSetByUser | ||||
| @@ -1327,8 +1319,8 @@ func (m *CMsgClientPersonaState_Friend) GetWatchingBroadcastTitle() string { | ||||
| } | ||||
|  | ||||
| type CMsgClientPersonaState_Friend_ClanData struct { | ||||
| 	OggAppId             *uint32  `protobuf:"varint,1,opt,name=ogg_app_id" json:"ogg_app_id,omitempty"` | ||||
| 	ChatGroupId          *uint64  `protobuf:"varint,2,opt,name=chat_group_id" json:"chat_group_id,omitempty"` | ||||
| 	OggAppId             *uint32  `protobuf:"varint,1,opt,name=ogg_app_id,json=oggAppId" json:"ogg_app_id,omitempty"` | ||||
| 	ChatGroupId          *uint64  `protobuf:"varint,2,opt,name=chat_group_id,json=chatGroupId" json:"chat_group_id,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -1423,7 +1415,7 @@ func (m *CMsgClientPersonaState_Friend_KV) GetValue() string { | ||||
| } | ||||
|  | ||||
| type CMsgClientFriendProfileInfo struct { | ||||
| 	SteamidFriend        *uint64  `protobuf:"fixed64,1,opt,name=steamid_friend" json:"steamid_friend,omitempty"` | ||||
| 	SteamidFriend        *uint64  `protobuf:"fixed64,1,opt,name=steamid_friend,json=steamidFriend" json:"steamid_friend,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -1463,12 +1455,12 @@ func (m *CMsgClientFriendProfileInfo) GetSteamidFriend() uint64 { | ||||
|  | ||||
| type CMsgClientFriendProfileInfoResponse struct { | ||||
| 	Eresult              *int32   `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||
| 	SteamidFriend        *uint64  `protobuf:"fixed64,2,opt,name=steamid_friend" json:"steamid_friend,omitempty"` | ||||
| 	TimeCreated          *uint32  `protobuf:"varint,3,opt,name=time_created" json:"time_created,omitempty"` | ||||
| 	RealName             *string  `protobuf:"bytes,4,opt,name=real_name" json:"real_name,omitempty"` | ||||
| 	CityName             *string  `protobuf:"bytes,5,opt,name=city_name" json:"city_name,omitempty"` | ||||
| 	StateName            *string  `protobuf:"bytes,6,opt,name=state_name" json:"state_name,omitempty"` | ||||
| 	CountryName          *string  `protobuf:"bytes,7,opt,name=country_name" json:"country_name,omitempty"` | ||||
| 	SteamidFriend        *uint64  `protobuf:"fixed64,2,opt,name=steamid_friend,json=steamidFriend" json:"steamid_friend,omitempty"` | ||||
| 	TimeCreated          *uint32  `protobuf:"varint,3,opt,name=time_created,json=timeCreated" json:"time_created,omitempty"` | ||||
| 	RealName             *string  `protobuf:"bytes,4,opt,name=real_name,json=realName" json:"real_name,omitempty"` | ||||
| 	CityName             *string  `protobuf:"bytes,5,opt,name=city_name,json=cityName" json:"city_name,omitempty"` | ||||
| 	StateName            *string  `protobuf:"bytes,6,opt,name=state_name,json=stateName" json:"state_name,omitempty"` | ||||
| 	CountryName          *string  `protobuf:"bytes,7,opt,name=country_name,json=countryName" json:"country_name,omitempty"` | ||||
| 	Headline             *string  `protobuf:"bytes,8,opt,name=headline" json:"headline,omitempty"` | ||||
| 	Summary              *string  `protobuf:"bytes,9,opt,name=summary" json:"summary,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| @@ -1569,7 +1561,7 @@ func (m *CMsgClientFriendProfileInfoResponse) GetSummary() string { | ||||
| type CMsgClientCreateFriendsGroup struct { | ||||
| 	Steamid              *uint64  `protobuf:"fixed64,1,opt,name=steamid" json:"steamid,omitempty"` | ||||
| 	Groupname            *string  `protobuf:"bytes,2,opt,name=groupname" json:"groupname,omitempty"` | ||||
| 	SteamidFriends       []uint64 `protobuf:"fixed64,3,rep,name=steamid_friends" json:"steamid_friends,omitempty"` | ||||
| 	SteamidFriends       []uint64 `protobuf:"fixed64,3,rep,name=steamid_friends,json=steamidFriends" json:"steamid_friends,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -1757,8 +1749,8 @@ func (m *CMsgClientDeleteFriendsGroupResponse) GetEresult() uint32 { | ||||
| type CMsgClientManageFriendsGroup struct { | ||||
| 	Groupid               *int32   `protobuf:"varint,1,opt,name=groupid" json:"groupid,omitempty"` | ||||
| 	Groupname             *string  `protobuf:"bytes,2,opt,name=groupname" json:"groupname,omitempty"` | ||||
| 	SteamidFriendsAdded   []uint64 `protobuf:"fixed64,3,rep,name=steamid_friends_added" json:"steamid_friends_added,omitempty"` | ||||
| 	SteamidFriendsRemoved []uint64 `protobuf:"fixed64,4,rep,name=steamid_friends_removed" json:"steamid_friends_removed,omitempty"` | ||||
| 	SteamidFriendsAdded   []uint64 `protobuf:"fixed64,3,rep,name=steamid_friends_added,json=steamidFriendsAdded" json:"steamid_friends_added,omitempty"` | ||||
| 	SteamidFriendsRemoved []uint64 `protobuf:"fixed64,4,rep,name=steamid_friends_removed,json=steamidFriendsRemoved" json:"steamid_friends_removed,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral  struct{} `json:"-"` | ||||
| 	XXX_unrecognized      []byte   `json:"-"` | ||||
| 	XXX_sizecache         int32    `json:"-"` | ||||
| @@ -2063,6 +2055,8 @@ var xxx_messageInfo_CMsgClientGetEmoticonList proto.InternalMessageInfo | ||||
|  | ||||
| type CMsgClientEmoticonList struct { | ||||
| 	Emoticons            []*CMsgClientEmoticonList_Emoticon `protobuf:"bytes,1,rep,name=emoticons" json:"emoticons,omitempty"` | ||||
| 	Stickers             []*CMsgClientEmoticonList_Sticker  `protobuf:"bytes,2,rep,name=stickers" json:"stickers,omitempty"` | ||||
| 	Effects              []*CMsgClientEmoticonList_Effect   `protobuf:"bytes,3,rep,name=effects" json:"effects,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}                           `json:"-"` | ||||
| 	XXX_unrecognized     []byte                             `json:"-"` | ||||
| 	XXX_sizecache        int32                              `json:"-"` | ||||
| @@ -2100,12 +2094,26 @@ func (m *CMsgClientEmoticonList) GetEmoticons() []*CMsgClientEmoticonList_Emotic | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList) GetStickers() []*CMsgClientEmoticonList_Sticker { | ||||
| 	if m != nil { | ||||
| 		return m.Stickers | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList) GetEffects() []*CMsgClientEmoticonList_Effect { | ||||
| 	if m != nil { | ||||
| 		return m.Effects | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type CMsgClientEmoticonList_Emoticon struct { | ||||
| 	Name                 *string  `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` | ||||
| 	Count                *int32   `protobuf:"varint,2,opt,name=count" json:"count,omitempty"` | ||||
| 	TimeLastUsed         *uint32  `protobuf:"varint,3,opt,name=time_last_used" json:"time_last_used,omitempty"` | ||||
| 	UseCount             *uint32  `protobuf:"varint,4,opt,name=use_count" json:"use_count,omitempty"` | ||||
| 	TimeReceived         *uint32  `protobuf:"varint,5,opt,name=time_received" json:"time_received,omitempty"` | ||||
| 	TimeLastUsed         *uint32  `protobuf:"varint,3,opt,name=time_last_used,json=timeLastUsed" json:"time_last_used,omitempty"` | ||||
| 	UseCount             *uint32  `protobuf:"varint,4,opt,name=use_count,json=useCount" json:"use_count,omitempty"` | ||||
| 	TimeReceived         *uint32  `protobuf:"varint,5,opt,name=time_received,json=timeReceived" json:"time_received,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -2171,6 +2179,140 @@ func (m *CMsgClientEmoticonList_Emoticon) GetTimeReceived() uint32 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| type CMsgClientEmoticonList_Sticker struct { | ||||
| 	Name                 *string  `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` | ||||
| 	Count                *int32   `protobuf:"varint,2,opt,name=count" json:"count,omitempty"` | ||||
| 	TimeReceived         *uint32  `protobuf:"varint,3,opt,name=time_received,json=timeReceived" json:"time_received,omitempty"` | ||||
| 	Appid                *uint32  `protobuf:"varint,4,opt,name=appid" json:"appid,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Sticker) Reset()         { *m = CMsgClientEmoticonList_Sticker{} } | ||||
| func (m *CMsgClientEmoticonList_Sticker) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientEmoticonList_Sticker) ProtoMessage()    {} | ||||
| func (*CMsgClientEmoticonList_Sticker) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_4f7c18b08a29999e, []int{28, 1} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Sticker) XXX_Unmarshal(b []byte) error { | ||||
| 	return xxx_messageInfo_CMsgClientEmoticonList_Sticker.Unmarshal(m, b) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Sticker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	return xxx_messageInfo_CMsgClientEmoticonList_Sticker.Marshal(b, m, deterministic) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Sticker) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_CMsgClientEmoticonList_Sticker.Merge(m, src) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Sticker) XXX_Size() int { | ||||
| 	return xxx_messageInfo_CMsgClientEmoticonList_Sticker.Size(m) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Sticker) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_CMsgClientEmoticonList_Sticker.DiscardUnknown(m) | ||||
| } | ||||
|  | ||||
| var xxx_messageInfo_CMsgClientEmoticonList_Sticker proto.InternalMessageInfo | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Sticker) GetName() string { | ||||
| 	if m != nil && m.Name != nil { | ||||
| 		return *m.Name | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Sticker) GetCount() int32 { | ||||
| 	if m != nil && m.Count != nil { | ||||
| 		return *m.Count | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Sticker) GetTimeReceived() uint32 { | ||||
| 	if m != nil && m.TimeReceived != nil { | ||||
| 		return *m.TimeReceived | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Sticker) GetAppid() uint32 { | ||||
| 	if m != nil && m.Appid != nil { | ||||
| 		return *m.Appid | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| type CMsgClientEmoticonList_Effect struct { | ||||
| 	Name                 *string  `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` | ||||
| 	Count                *int32   `protobuf:"varint,2,opt,name=count" json:"count,omitempty"` | ||||
| 	TimeReceived         *uint32  `protobuf:"varint,3,opt,name=time_received,json=timeReceived" json:"time_received,omitempty"` | ||||
| 	InfiniteUse          *bool    `protobuf:"varint,4,opt,name=infinite_use,json=infiniteUse" json:"infinite_use,omitempty"` | ||||
| 	Appid                *uint32  `protobuf:"varint,5,opt,name=appid" json:"appid,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) Reset()         { *m = CMsgClientEmoticonList_Effect{} } | ||||
| func (m *CMsgClientEmoticonList_Effect) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientEmoticonList_Effect) ProtoMessage()    {} | ||||
| func (*CMsgClientEmoticonList_Effect) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_4f7c18b08a29999e, []int{28, 2} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) XXX_Unmarshal(b []byte) error { | ||||
| 	return xxx_messageInfo_CMsgClientEmoticonList_Effect.Unmarshal(m, b) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Effect) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	return xxx_messageInfo_CMsgClientEmoticonList_Effect.Marshal(b, m, deterministic) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Effect) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_CMsgClientEmoticonList_Effect.Merge(m, src) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Effect) XXX_Size() int { | ||||
| 	return xxx_messageInfo_CMsgClientEmoticonList_Effect.Size(m) | ||||
| } | ||||
| func (m *CMsgClientEmoticonList_Effect) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_CMsgClientEmoticonList_Effect.DiscardUnknown(m) | ||||
| } | ||||
|  | ||||
| var xxx_messageInfo_CMsgClientEmoticonList_Effect proto.InternalMessageInfo | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) GetName() string { | ||||
| 	if m != nil && m.Name != nil { | ||||
| 		return *m.Name | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) GetCount() int32 { | ||||
| 	if m != nil && m.Count != nil { | ||||
| 		return *m.Count | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) GetTimeReceived() uint32 { | ||||
| 	if m != nil && m.TimeReceived != nil { | ||||
| 		return *m.TimeReceived | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) GetInfiniteUse() bool { | ||||
| 	if m != nil && m.InfiniteUse != nil { | ||||
| 		return *m.InfiniteUse | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientEmoticonList_Effect) GetAppid() uint32 { | ||||
| 	if m != nil && m.Appid != nil { | ||||
| 		return *m.Appid | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	proto.RegisterType((*CMsgClientFriendMsg)(nil), "CMsgClientFriendMsg") | ||||
| 	proto.RegisterType((*CMsgClientFriendMsgIncoming)(nil), "CMsgClientFriendMsgIncoming") | ||||
| @@ -2209,6 +2351,8 @@ func init() { | ||||
| 	proto.RegisterType((*CMsgClientGetEmoticonList)(nil), "CMsgClientGetEmoticonList") | ||||
| 	proto.RegisterType((*CMsgClientEmoticonList)(nil), "CMsgClientEmoticonList") | ||||
| 	proto.RegisterType((*CMsgClientEmoticonList_Emoticon)(nil), "CMsgClientEmoticonList.Emoticon") | ||||
| 	proto.RegisterType((*CMsgClientEmoticonList_Sticker)(nil), "CMsgClientEmoticonList.Sticker") | ||||
| 	proto.RegisterType((*CMsgClientEmoticonList_Effect)(nil), "CMsgClientEmoticonList.Effect") | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| @@ -2216,106 +2360,141 @@ func init() { | ||||
| } | ||||
|  | ||||
| var fileDescriptor_4f7c18b08a29999e = []byte{ | ||||
| 	// 1607 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0x4b, 0x73, 0x1b, 0xc7, | ||||
| 	0x11, 0xce, 0x8a, 0x24, 0x1e, 0x0d, 0x82, 0xa4, 0x96, 0x0f, 0xad, 0x40, 0x3d, 0x90, 0xb5, 0xab, | ||||
| 	0x8c, 0x4a, 0x25, 0x70, 0x8a, 0x2a, 0xd9, 0x8e, 0x2a, 0x0f, 0xd9, 0x54, 0x2c, 0x2a, 0x32, 0x1d, | ||||
| 	0x95, 0x94, 0xf2, 0x25, 0x87, 0xa9, 0xc1, 0x6e, 0x13, 0x98, 0xd2, 0xee, 0x2c, 0x3c, 0x33, 0x4b, | ||||
| 	0x07, 0xb7, 0x54, 0x8e, 0xf9, 0x0f, 0xf9, 0x1f, 0xa9, 0xdc, 0xf3, 0x27, 0x72, 0xc9, 0x3d, 0x97, | ||||
| 	0xfc, 0x82, 0x54, 0xa5, 0xa6, 0x67, 0x16, 0x5c, 0x10, 0x00, 0x69, 0xdf, 0x80, 0x9e, 0xe9, 0xaf, | ||||
| 	0xbf, 0xee, 0xe9, 0xd7, 0xc2, 0x40, 0x1b, 0xe4, 0x79, 0x8e, 0x5a, 0xf3, 0x31, 0x6a, 0x96, 0x64, | ||||
| 	0x02, 0xa5, 0xd1, 0xa8, 0x2e, 0x51, 0xb1, 0x0b, 0x25, 0x50, 0xa6, 0x7a, 0x38, 0x55, 0x85, 0x29, | ||||
| 	0x7a, 0xd1, 0xe2, 0xcd, 0x11, 0xd7, 0xe8, 0x4e, 0xe2, 0xbf, 0x06, 0xb0, 0x7f, 0x7a, 0xae, 0xc7, | ||||
| 	0xa7, 0xa4, 0xfc, 0x25, 0x69, 0x9d, 0xeb, 0x71, 0xb8, 0x0b, 0x4d, 0xd2, 0x11, 0x69, 0x14, 0xf4, | ||||
| 	0x83, 0x41, 0x23, 0xbc, 0x07, 0xbb, 0xc9, 0x84, 0x1b, 0x86, 0xd2, 0xa8, 0x19, 0x33, 0xb3, 0x29, | ||||
| 	0x46, 0x77, 0xfa, 0xc1, 0x60, 0xcb, 0xde, 0xf4, 0xc0, 0xd1, 0x46, 0x3f, 0x18, 0x6c, 0x87, 0x7d, | ||||
| 	0x88, 0x94, 0x11, 0x39, 0x3e, 0x39, 0x61, 0x9e, 0x8c, 0xfd, 0xa7, 0x0d, 0xcf, 0xa7, 0xd1, 0x66, | ||||
| 	0x3f, 0x18, 0x34, 0xc3, 0x23, 0xd8, 0xc1, 0x64, 0x52, 0x30, 0x53, 0x30, 0x8d, 0x32, 0x45, 0x15, | ||||
| 	0x6d, 0xf5, 0x83, 0x41, 0x2b, 0xfe, 0x5b, 0x00, 0xc7, 0x2b, 0xc8, 0xbc, 0x92, 0x49, 0x91, 0x0b, | ||||
| 	0x39, 0x0e, 0x0f, 0x60, 0xdb, 0x93, 0x62, 0x17, 0xaa, 0xc8, 0x6f, 0x63, 0xf6, 0x00, 0x0e, 0xec, | ||||
| 	0x35, 0x96, 0x89, 0x5c, 0x18, 0x4c, 0x19, 0x4f, 0x92, 0xa2, 0x94, 0x86, 0x68, 0xb6, 0xea, 0xbc, | ||||
| 	0x37, 0x6f, 0xe5, 0x6d, 0xf9, 0x35, 0xe3, 0xb7, 0xf5, 0x58, 0x7d, 0x9e, 0xa6, 0x8e, 0xa1, 0x75, | ||||
| 	0xa7, 0xa2, 0x65, 0x0a, 0xc6, 0xd3, 0x2a, 0x64, 0x1f, 0xc0, 0xb1, 0x37, 0x29, 0x79, 0x8e, 0xac, | ||||
| 	0x50, 0x0c, 0x73, 0x2e, 0xb2, 0xea, 0x92, 0x25, 0xd9, 0x8e, 0xb1, 0xee, 0xf2, 0x1c, 0xf3, 0x2d, | ||||
| 	0xea, 0x69, 0x21, 0x35, 0x86, 0x21, 0x34, 0x51, 0xa1, 0x2e, 0x33, 0x43, 0xa0, 0x5b, 0xcf, 0x82, | ||||
| 	0x93, 0xb9, 0x3d, 0x26, 0x52, 0x0b, 0x84, 0x0e, 0xaa, 0x11, 0xf6, 0x20, 0x9c, 0xa2, 0xd2, 0x85, | ||||
| 	0xe4, 0x8c, 0x0c, 0xba, 0xb3, 0x0d, 0x32, 0xf3, 0x13, 0x38, 0xba, 0x32, 0xf3, 0x16, 0xf3, 0xe2, | ||||
| 	0x12, 0x3d, 0xfb, 0x3d, 0x68, 0xb9, 0x64, 0xa9, 0x9e, 0x3a, 0xfe, 0x04, 0x0e, 0xae, 0xee, 0x9e, | ||||
| 	0x89, 0x74, 0xed, 0xcd, 0x70, 0x1b, 0x36, 0x27, 0x22, 0x75, 0xf1, 0x6e, 0xc5, 0xff, 0x09, 0xe0, | ||||
| 	0xf0, 0xfa, 0xf3, 0xe9, 0xaf, 0x84, 0x36, 0xf6, 0xe1, 0x46, 0x42, 0x26, 0x0a, 0x73, 0x94, 0x86, | ||||
| 	0x67, 0xa4, 0xdd, 0x0a, 0x87, 0xd0, 0xf4, 0x69, 0x1a, 0xdd, 0xe9, 0x6f, 0x0c, 0x3a, 0x27, 0x0f, | ||||
| 	0x87, 0x2b, 0xd5, 0x87, 0xde, 0x7e, 0x04, 0x7b, 0x39, 0xff, 0x93, 0x4f, 0x6d, 0x76, 0xf5, 0x96, | ||||
| 	0xdd, 0xf0, 0x18, 0xf6, 0x79, 0x62, 0xc4, 0x25, 0x2e, 0x1e, 0x6e, 0xd2, 0xe1, 0x7d, 0xb8, 0xeb, | ||||
| 	0xcd, 0xb8, 0x4c, 0x60, 0x13, 0x61, 0x5c, 0xc2, 0xf5, 0x7e, 0x01, 0x0d, 0x8f, 0x1d, 0x02, 0x94, | ||||
| 	0xd9, 0x35, 0xef, 0x8e, 0x61, 0x1f, 0x9d, 0x48, 0x61, 0xc6, 0x8d, 0x28, 0xa4, 0x9e, 0x88, 0x29, | ||||
| 	0x39, 0xdb, 0x8d, 0xff, 0x79, 0x67, 0x39, 0x57, 0xf5, 0x4b, 0x55, 0x94, 0x53, 0xe7, 0xf2, 0x1e, | ||||
| 	0xb4, 0x46, 0xca, 0xc6, 0x79, 0xee, 0xee, 0xf5, 0x20, 0x50, 0xd0, 0xc2, 0x5f, 0xc3, 0xb6, 0xb3, | ||||
| 	0xe1, 0x74, 0xa3, 0x0d, 0x8a, 0xc4, 0x60, 0x78, 0x03, 0xb6, 0x8f, 0x07, 0x09, 0xc2, 0x33, 0xe8, | ||||
| 	0xe4, 0x98, 0x8f, 0x50, 0x59, 0x6e, 0x3a, 0xda, 0x24, 0xf5, 0x27, 0xdf, 0x57, 0x5d, 0x9f, 0xcf, | ||||
| 	0x75, 0x7b, 0x4f, 0xa1, 0x53, 0x07, 0xde, 0x83, 0x96, 0xa4, 0x5f, 0xaf, 0x5e, 0xb8, 0xd4, 0x73, | ||||
| 	0xe5, 0xa7, 0x48, 0xf6, 0x35, 0xcf, 0xdd, 0xab, 0xb7, 0x7b, 0xbf, 0x82, 0xa3, 0xd5, 0x80, 0xe1, | ||||
| 	0x5d, 0x68, 0x97, 0xd9, 0x3b, 0x9b, 0xa9, 0x1e, 0xa2, 0xb1, 0x00, 0x4a, 0x45, 0x1a, 0xff, 0x23, | ||||
| 	0x80, 0x07, 0x57, 0x64, 0xdf, 0x64, 0x7c, 0x86, 0xea, 0x6b, 0x91, 0xbc, 0xb7, 0x19, 0x4c, 0x81, | ||||
| 	0xdc, 0x85, 0xe6, 0x62, 0x1c, 0xf7, 0xa1, 0xb3, 0x1c, 0xc6, 0xdf, 0x40, 0x5b, 0x7a, 0xad, 0x2a, | ||||
| 	0x86, 0x3f, 0x1d, 0xde, 0x84, 0x3b, 0x5c, 0x14, 0xf5, 0x9e, 0xc0, 0xce, 0xa2, 0x64, 0xb9, 0x05, | ||||
| 	0x5a, 0xf2, 0xfe, 0xd0, 0x57, 0xd5, 0xf3, 0x7a, 0x0e, 0xbc, 0x43, 0xf3, 0x43, 0x10, 0x5c, 0xf9, | ||||
| 	0x7f, 0x02, 0x1f, 0xdc, 0x80, 0x30, 0x6f, 0x03, 0xbb, 0x8b, 0x6d, 0xa0, 0x1b, 0xff, 0xbe, 0x6e, | ||||
| 	0xf9, 0x2d, 0x7e, 0x5b, 0xa2, 0xf6, 0x2f, 0xfd, 0x82, 0x1b, 0x1e, 0x3e, 0x86, 0x7b, 0x55, 0x2b, | ||||
| 	0xd0, 0x86, 0x1b, 0x64, 0xca, 0x5d, 0x41, 0xc7, 0xa4, 0x6b, 0x01, 0xeb, 0xb5, 0xd7, 0x88, 0xff, | ||||
| 	0x1b, 0xd4, 0x3b, 0xc4, 0xe9, 0x84, 0xcb, 0x31, 0xbe, 0x33, 0xdc, 0x94, 0x3a, 0x3c, 0x84, 0xee, | ||||
| 	0x02, 0x98, 0x87, 0xd8, 0x87, 0xce, 0x94, 0xd8, 0xb2, 0x2b, 0x7f, 0xc2, 0x47, 0x70, 0x24, 0x34, | ||||
| 	0xe3, 0xa5, 0x29, 0xd8, 0x18, 0x25, 0x2a, 0x6e, 0x1b, 0xef, 0x3c, 0x62, 0x2d, 0x8b, 0x35, 0x11, | ||||
| 	0xe3, 0x09, 0x9b, 0x2a, 0x51, 0x28, 0x61, 0x66, 0x54, 0xa3, 0x2d, 0x5b, 0x6a, 0x73, 0x13, 0x68, | ||||
| 	0xd8, 0x68, 0xc6, 0x4a, 0x5d, 0x8d, 0x85, 0xf0, 0x51, 0xed, 0x90, 0x9c, 0xb9, 0xc8, 0xf8, 0x58, | ||||
| 	0x47, 0x0d, 0xcb, 0xe2, 0x59, 0xf0, 0xf3, 0xf0, 0x21, 0x1c, 0x4a, 0xc4, 0x94, 0x55, 0x97, 0x94, | ||||
| 	0x8f, 0x5a, 0xd4, 0x24, 0xf5, 0x23, 0xd8, 0x11, 0xd5, 0x74, 0x64, 0x22, 0xcd, 0x30, 0x6a, 0x51, | ||||
| 	0xbb, 0x7a, 0x0e, 0xf7, 0xad, 0xc3, 0x6f, 0x9c, 0x96, 0xf3, 0x78, 0x1e, 0xf0, 0x1d, 0x68, 0xd4, | ||||
| 	0xe3, 0xbd, 0xd2, 0xd9, 0xf8, 0xdf, 0xcd, 0x7a, 0xcc, 0x3c, 0x90, 0x0d, 0x1a, 0xba, 0x5a, 0xb1, | ||||
| 	0xd1, 0xf3, 0x64, 0x1d, 0xca, 0xc7, 0xd7, 0x3b, 0xde, 0xa3, 0xe1, 0x6a, 0x7d, 0x5f, 0xa3, 0xbd, | ||||
| 	0xff, 0x35, 0xe6, 0x1d, 0x6a, 0xb9, 0xfb, 0x2e, 0xbd, 0x0b, 0x75, 0x26, 0x3b, 0x06, 0xc6, 0xb6, | ||||
| 	0xfd, 0x13, 0xdf, 0x94, 0xf1, 0xe9, 0x94, 0x89, 0xd4, 0x37, 0xca, 0x23, 0xd8, 0xa1, 0x33, 0x3f, | ||||
| 	0xe0, 0xc4, 0xd4, 0xf7, 0xc8, 0x08, 0xf6, 0xea, 0xf2, 0x69, 0xa1, 0x5c, 0x8b, 0xec, 0x2e, 0xbc, | ||||
| 	0xcc, 0xf5, 0xe0, 0xdb, 0x91, 0x59, 0xc8, 0x4c, 0x48, 0xab, 0xa8, 0xb5, 0x28, 0x24, 0x13, 0x52, | ||||
| 	0x1b, 0x2e, 0x13, 0xd4, 0x14, 0xfc, 0xae, 0x7d, 0x9b, 0x69, 0x39, 0xca, 0x84, 0x9e, 0x60, 0x3a, | ||||
| 	0x3f, 0xb4, 0x7c, 0x5a, 0x4b, 0xe8, 0xb5, 0x77, 0x87, 0xaa, 0xd0, 0xeb, 0x31, 0xdf, 0xa5, 0x04, | ||||
| 	0x0b, 0x01, 0xbe, 0x2d, 0x51, 0xcd, 0x1c, 0xc7, 0x83, 0xca, 0xab, 0x6a, 0x00, 0xeb, 0xa2, 0x54, | ||||
| 	0x09, 0x46, 0xf7, 0x29, 0x40, 0xfb, 0xd0, 0xe1, 0x97, 0xdc, 0x70, 0xc5, 0x26, 0x5c, 0x4f, 0xa2, | ||||
| 	0xc7, 0x34, 0xe6, 0xf7, 0xa1, 0x93, 0x71, 0x6d, 0x58, 0x56, 0x8c, 0x8b, 0x8b, 0x8b, 0xe8, 0x67, | ||||
| 	0x84, 0x10, 0x02, 0xcc, 0x85, 0x32, 0x1a, 0x56, 0x31, 0x21, 0x99, 0x46, 0x94, 0xcc, 0xb9, 0x19, | ||||
| 	0x7d, 0x4c, 0x27, 0x77, 0xa1, 0x9d, 0x64, 0x5c, 0x32, 0xc5, 0xe5, 0xfb, 0xe8, 0xa4, 0x12, 0x51, | ||||
| 	0x00, 0x89, 0xe9, 0xa7, 0xc4, 0x74, 0x07, 0x1a, 0x56, 0x24, 0xd2, 0xe8, 0x33, 0x62, 0x53, 0xc5, | ||||
| 	0x3e, 0xe5, 0x86, 0xb3, 0x51, 0x56, 0x8c, 0xa2, 0x5f, 0x12, 0xa1, 0x67, 0x1e, 0xcd, 0xca, 0xa3, | ||||
| 	0xe7, 0xfd, 0x60, 0xd0, 0x39, 0xf9, 0xe8, 0xe6, 0xb4, 0x18, 0x9e, 0x66, 0x5c, 0x52, 0x9d, 0xef, | ||||
| 	0x41, 0x8b, 0x74, 0x0d, 0x1f, 0x47, 0x9f, 0x93, 0xd5, 0xcf, 0xa0, 0xab, 0x44, 0x62, 0x0b, 0x0c, | ||||
| 	0x35, 0xca, 0x04, 0xa3, 0x97, 0x94, 0x68, 0x3f, 0xbe, 0x05, 0xf1, 0xf5, 0x37, 0x34, 0x9f, 0x54, | ||||
| 	0xc1, 0xd3, 0xc4, 0x3a, 0x2d, 0xd2, 0xe8, 0xac, 0x4a, 0x32, 0x62, 0x9d, 0x15, 0xa3, 0xd1, 0xcc, | ||||
| 	0x8a, 0x5f, 0x91, 0xf8, 0x43, 0x78, 0xf0, 0x1d, 0x37, 0xc9, 0x44, 0xc8, 0x31, 0xbb, 0xd2, 0xf2, | ||||
| 	0xeb, 0x8e, 0x48, 0xa3, 0xdf, 0x55, 0xf9, 0xb1, 0xea, 0xd6, 0x74, 0x2a, 0xd2, 0xe8, 0x35, 0xdd, | ||||
| 	0x88, 0xa1, 0xb7, 0xe2, 0xc6, 0xa5, 0xc0, 0xef, 0x50, 0xe9, 0xe8, 0xab, 0x1b, 0x50, 0x8c, 0x30, | ||||
| 	0x19, 0x46, 0xe7, 0x34, 0x83, 0x9e, 0x42, 0x6b, 0x1e, 0x92, 0x10, 0xa0, 0x18, 0x8f, 0xab, 0xb4, | ||||
| 	0x77, 0x75, 0x77, 0x08, 0x5d, 0x5a, 0x11, 0xc7, 0x76, 0xf4, 0x58, 0xb1, 0xad, 0x94, 0xcd, 0x5e, | ||||
| 	0x1f, 0xee, 0xbc, 0xfe, 0x26, 0xec, 0xc0, 0xc6, 0x7b, 0x9c, 0xd1, 0xcd, 0x76, 0xd8, 0x85, 0xad, | ||||
| 	0x4b, 0x9e, 0x95, 0x55, 0x85, 0x3f, 0x5d, 0x1e, 0xf2, 0x6f, 0x54, 0x71, 0x21, 0x32, 0x7c, 0x25, | ||||
| 	0x2f, 0x8a, 0x7a, 0xe2, 0xb9, 0xda, 0xf4, 0x1b, 0xd4, 0xbf, 0x82, 0x7a, 0x5b, 0x5f, 0xd2, 0xfb, | ||||
| 	0x5e, 0xdb, 0xdd, 0x15, 0xa6, 0xdb, 0xee, 0x0e, 0x60, 0xdb, 0xee, 0xa3, 0x2c, 0x51, 0x68, 0x9b, | ||||
| 	0xaa, 0x2f, 0xe8, 0xbb, 0xd0, 0x56, 0xc8, 0x33, 0x97, 0x77, 0x9b, 0xe4, 0x82, 0xcd, 0x4e, 0x61, | ||||
| 	0x66, 0x4e, 0xb4, 0x55, 0x15, 0x8d, 0x2b, 0x5e, 0x92, 0x35, 0x48, 0x76, 0x00, 0xdb, 0xf4, 0x58, | ||||
| 	0xca, 0xdf, 0x6c, 0x92, 0x74, 0x0f, 0x5a, 0x13, 0xe4, 0x29, 0x25, 0x7b, 0x8b, 0x24, 0x76, 0x88, | ||||
| 	0x95, 0x79, 0xce, 0xd5, 0x2c, 0x6a, 0x53, 0x4c, 0xfe, 0x58, 0x1f, 0xd8, 0xa7, 0xc4, 0xa6, 0xbe, | ||||
| 	0x63, 0x2c, 0x4f, 0x3d, 0x5b, 0x1b, 0xf6, 0xa4, 0x36, 0x26, 0xee, 0xc1, 0xee, 0xa2, 0x93, 0x6e, | ||||
| 	0x68, 0x37, 0xe2, 0x33, 0xf8, 0xf0, 0x26, 0xf0, 0xb5, 0x03, 0xd1, 0x0a, 0xc8, 0x88, 0x7f, 0xdc, | ||||
| 	0xad, 0xf8, 0x79, 0x9d, 0xe6, 0x0b, 0xcc, 0xf0, 0x36, 0x9a, 0x4b, 0x08, 0x9f, 0xd6, 0xb9, 0x2c, | ||||
| 	0x23, 0xac, 0x1f, 0xce, 0x7f, 0x59, 0xd8, 0x69, 0xce, 0xb9, 0xe4, 0xe3, 0x25, 0xdb, 0x95, 0x29, | ||||
| 	0xb7, 0x5a, 0xad, 0x08, 0xd1, 0x43, 0x38, 0xbc, 0x16, 0xa2, 0xf9, 0x42, 0xbf, 0x31, 0x68, 0xd8, | ||||
| 	0x09, 0x7f, 0xfd, 0x98, 0xd6, 0x24, 0x4c, 0x69, 0x07, 0x6c, 0x2c, 0xb2, 0x5f, 0xe6, 0xb0, 0x9e, | ||||
| 	0xfd, 0x17, 0xd0, 0x5b, 0xf1, 0x45, 0xf2, 0x87, 0x62, 0x0d, 0xf5, 0x7d, 0xe8, 0x78, 0x22, 0xd4, | ||||
| 	0xba, 0x29, 0x59, 0xe3, 0xa7, 0x10, 0xaf, 0xc7, 0x58, 0x6f, 0xfa, 0x25, 0x3c, 0x5e, 0xfd, 0x95, | ||||
| 	0xf2, 0xa5, 0x2a, 0xf2, 0x1f, 0x62, 0xff, 0x19, 0x7c, 0x74, 0x0b, 0xd0, 0x7a, 0x12, 0xc7, 0x6e, | ||||
| 	0x2f, 0x70, 0xba, 0x2f, 0xd1, 0xfc, 0x36, 0x2f, 0x8c, 0x48, 0x0a, 0x69, 0xb7, 0xc6, 0xf8, 0xef, | ||||
| 	0x0b, 0x6b, 0x52, 0xfd, 0x28, 0x7c, 0x02, 0x6d, 0xf4, 0xff, 0xed, 0xbc, 0xb7, 0x5d, 0xb7, 0x3f, | ||||
| 	0x5c, 0x7d, 0x77, 0x58, 0xfd, 0xe9, 0x09, 0x68, 0x55, 0xbf, 0xed, 0xd7, 0x14, 0xbd, 0xff, 0xbc, | ||||
| 	0x13, 0xb9, 0xaf, 0x18, 0xf7, 0x31, 0x7b, 0x04, 0x3b, 0x54, 0xfe, 0x34, 0x92, 0x4a, 0x5d, 0x6f, | ||||
| 	0x00, 0xa5, 0xc6, 0x85, 0x0f, 0x9e, 0x43, 0xe8, 0xd2, 0x55, 0x85, 0x09, 0x0a, 0x9b, 0x10, 0x34, | ||||
| 	0xc9, 0xbf, 0xd8, 0x3a, 0x0b, 0xfe, 0x1c, 0xfc, 0xe8, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbe, | ||||
| 	0x80, 0x1e, 0x5f, 0x36, 0x10, 0x00, 0x00, | ||||
| 	// 2171 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xdd, 0x6e, 0x1b, 0xb9, | ||||
| 	0x15, 0xae, 0xe4, 0x3f, 0xf9, 0x48, 0x72, 0x36, 0x74, 0xe2, 0x4c, 0x94, 0x6c, 0xe2, 0x4c, 0xd2, | ||||
| 	0xc6, 0x68, 0xb3, 0x6a, 0xea, 0x14, 0xbb, 0xc1, 0x36, 0xd8, 0xc6, 0xb1, 0xe3, 0x44, 0x1b, 0x7b, | ||||
| 	0xd7, 0x18, 0x39, 0x41, 0xb1, 0x40, 0x31, 0xa0, 0x66, 0x28, 0x89, 0xf0, 0xfc, 0x68, 0x49, 0xca, | ||||
| 	0xbb, 0xba, 0x6a, 0x9f, 0xa0, 0xe8, 0x03, 0xf4, 0xa6, 0x17, 0xbd, 0xe8, 0x4b, 0xf4, 0xa2, 0x6f, | ||||
| 	0xd0, 0x9b, 0x3e, 0x46, 0x81, 0x02, 0x7d, 0x80, 0xe2, 0x90, 0x9c, 0xd1, 0x8c, 0x25, 0xff, 0xf4, | ||||
| 	0xe7, 0x6e, 0xf8, 0x9d, 0xff, 0x43, 0x9e, 0xc3, 0xc3, 0x81, 0x2d, 0xa9, 0x18, 0x8d, 0x63, 0x26, | ||||
| 	0x25, 0x1d, 0x30, 0xe9, 0x07, 0x11, 0x67, 0x89, 0x92, 0x4c, 0x9c, 0x32, 0xe1, 0xf7, 0x05, 0x67, | ||||
| 	0x49, 0x28, 0xdb, 0x23, 0x91, 0xaa, 0xb4, 0xe5, 0x94, 0x39, 0x7b, 0x54, 0x32, 0x43, 0x71, 0xff, | ||||
| 	0x56, 0x81, 0xf5, 0xdd, 0x43, 0x39, 0xd8, 0xd5, 0xc2, 0xfb, 0x5a, 0xea, 0x50, 0x0e, 0x88, 0x03, | ||||
| 	0x2b, 0x5a, 0x86, 0x87, 0x4e, 0x65, 0xb3, 0xb2, 0xb5, 0xec, 0x65, 0x4b, 0xf2, 0x23, 0xb8, 0x16, | ||||
| 	0x0c, 0xa9, 0xf2, 0x59, 0xa2, 0xc4, 0xc4, 0x57, 0x93, 0x11, 0x73, 0xaa, 0x9b, 0x95, 0xad, 0x25, | ||||
| 	0xaf, 0x89, 0xf0, 0x6b, 0x44, 0x8f, 0x27, 0x23, 0x86, 0x1a, 0xac, 0x41, 0x67, 0x61, 0xb3, 0xb2, | ||||
| 	0xd5, 0xf0, 0xb2, 0x25, 0x79, 0x0e, 0x8e, 0x50, 0x3c, 0x66, 0xcf, 0xb6, 0x7d, 0xeb, 0x2d, 0xae, | ||||
| 	0xa4, 0xa2, 0xf1, 0xc8, 0x59, 0xdc, 0xac, 0x6c, 0xad, 0x78, 0x1b, 0x96, 0xde, 0xd5, 0xe4, 0xe3, | ||||
| 	0x8c, 0x4a, 0x1e, 0xc1, 0x1a, 0x0b, 0x86, 0xa9, 0xaf, 0x52, 0x5f, 0xb2, 0x24, 0x64, 0xc2, 0x59, | ||||
| 	0xda, 0xac, 0x6c, 0xd5, 0xbc, 0x06, 0xa2, 0xc7, 0x69, 0x57, 0x63, 0xee, 0x3f, 0x2a, 0x70, 0x67, | ||||
| 	0x4e, 0x4c, 0x9d, 0x24, 0x48, 0x63, 0x9e, 0x0c, 0xc8, 0x03, 0x68, 0xd8, 0x60, 0xfc, 0xbe, 0x48, | ||||
| 	0x63, 0x1b, 0x60, 0xdd, 0x62, 0xfb, 0x22, 0x8d, 0xaf, 0x1c, 0xe4, 0x53, 0xb8, 0x81, 0x2a, 0xfc, | ||||
| 	0x88, 0xc7, 0x5c, 0xb1, 0xd0, 0xa7, 0x41, 0x90, 0x8e, 0x13, 0xa5, 0x23, 0xae, 0x79, 0x04, 0x69, | ||||
| 	0x07, 0x86, 0xb4, 0x63, 0x28, 0xc5, 0xb4, 0x2c, 0x5e, 0x3d, 0x2d, 0x4b, 0x17, 0xa5, 0xc5, 0x9d, | ||||
| 	0x14, 0xf7, 0x70, 0x27, 0x0c, 0x4d, 0xc8, 0x98, 0xad, 0x2c, 0x4e, 0x95, 0xfa, 0x34, 0xcc, 0xb6, | ||||
| 	0x32, 0x8b, 0xfe, 0x38, 0xdd, 0x09, 0x43, 0xf2, 0x02, 0xee, 0x58, 0xaf, 0x13, 0x1a, 0x33, 0x3f, | ||||
| 	0x15, 0x3e, 0x8b, 0x29, 0x8f, 0x32, 0x11, 0x0c, 0x7b, 0xd5, 0xbb, 0x55, 0x60, 0xf9, 0x5a, 0xbc, | ||||
| 	0x46, 0x06, 0x2d, 0xed, 0xfe, 0xae, 0x94, 0xeb, 0xdc, 0xb6, 0xc7, 0xe4, 0x28, 0x4d, 0x24, 0x23, | ||||
| 	0x77, 0x60, 0x85, 0x09, 0x26, 0xc7, 0x91, 0xd2, 0xc6, 0x97, 0x3e, 0xaf, 0x6c, 0x7b, 0x19, 0x92, | ||||
| 	0x3b, 0xe8, 0xf3, 0x10, 0x6d, 0x31, 0x63, 0x2d, 0x73, 0xb0, 0x13, 0xee, 0x20, 0x46, 0x9e, 0x00, | ||||
| 	0x19, 0x31, 0x21, 0xd3, 0x84, 0xfa, 0xda, 0x43, 0xc3, 0xb9, 0xa0, 0xfd, 0xfa, 0xc8, 0x52, 0xbe, | ||||
| 	0xa2, 0x31, 0xd3, 0xdc, 0xee, 0xcf, 0x61, 0x63, 0xea, 0x8f, 0xc7, 0xe2, 0xf4, 0x94, 0xd9, 0x74, | ||||
| 	0xb4, 0xa0, 0x66, 0xaa, 0x22, 0x3f, 0xd3, 0xf9, 0xda, 0xdd, 0x87, 0x1b, 0x53, 0xa9, 0xb7, 0x3c, | ||||
| 	0xbc, 0x82, 0x0c, 0x21, 0xb0, 0x38, 0xe4, 0xa1, 0x39, 0x18, 0x35, 0x4f, 0x7f, 0xbb, 0x7f, 0xaf, | ||||
| 	0xc2, 0xcd, 0xb3, 0x47, 0x4f, 0x1e, 0x70, 0xa9, 0x88, 0x0b, 0x8d, 0x1e, 0x4f, 0x02, 0xc1, 0x62, | ||||
| 	0x96, 0x28, 0x1a, 0x69, 0x6d, 0x35, 0xaf, 0x84, 0x91, 0xcf, 0x60, 0xc5, 0xd6, 0xad, 0x53, 0xdd, | ||||
| 	0x5c, 0xd8, 0xaa, 0x6f, 0x7f, 0xdc, 0x9e, 0xab, 0xac, 0x6d, 0x93, 0x9c, 0x71, 0x93, 0x2d, 0xf8, | ||||
| 	0x28, 0xa6, 0xdf, 0xdb, 0xa2, 0xf7, 0xa7, 0x47, 0xb0, 0xe9, 0xad, 0xc5, 0xf4, 0x7b, 0xc3, 0xbd, | ||||
| 	0xab, 0x8f, 0x5f, 0x1b, 0xd6, 0x69, 0xa0, 0xf8, 0x29, 0x2b, 0x33, 0x2f, 0x6a, 0xe6, 0xeb, 0x86, | ||||
| 	0x54, 0xe4, 0xff, 0x31, 0x5c, 0xb7, 0x46, 0xcc, 0x19, 0xf7, 0x87, 0x5c, 0xd9, 0xa2, 0xbb, 0xd6, | ||||
| 	0xcf, 0x3c, 0x8a, 0xb9, 0x7a, 0xcb, 0x55, 0xeb, 0x1b, 0x58, 0xb6, 0x69, 0xbb, 0x07, 0x30, 0x8e, | ||||
| 	0xce, 0x24, 0xae, 0x80, 0x90, 0xa7, 0xb0, 0xce, 0xcc, 0x42, 0xb0, 0x88, 0x2a, 0x9e, 0x26, 0x72, | ||||
| 	0xc8, 0x47, 0x3a, 0x93, 0x4d, 0x6f, 0x1e, 0xc9, 0xfd, 0xd3, 0xc2, 0x6c, 0x4d, 0xcb, 0x37, 0x22, | ||||
| 	0x1d, 0x8f, 0x4c, 0x7a, 0x5b, 0x50, 0xeb, 0x09, 0xdc, 0xed, 0x3c, 0xb5, 0xf9, 0x7a, 0x26, 0xf5, | ||||
| 	0xd5, 0x39, 0xa9, 0x3f, 0x80, 0x86, 0xb1, 0x6a, 0x74, 0x3a, 0x0b, 0x3a, 0xff, 0x5b, 0xed, 0x0b, | ||||
| 	0x6c, 0xda, 0x5d, 0xd0, 0x80, 0x57, 0x92, 0x26, 0xef, 0xa1, 0x1e, 0xb3, 0xb8, 0xc7, 0x04, 0xfa, | ||||
| 	0x2e, 0x9d, 0x45, 0xad, 0xec, 0xd9, 0x55, 0x95, 0xc9, 0xc3, 0x5c, 0xd6, 0x2b, 0xea, 0x69, 0x1d, | ||||
| 	0x42, 0xbd, 0xc0, 0x86, 0x31, 0x27, 0xfa, 0xab, 0xb3, 0x67, 0x8a, 0xcb, 0xcb, 0xd7, 0x18, 0xb3, | ||||
| 	0x54, 0x42, 0xaf, 0xb0, 0x36, 0x6c, 0x19, 0x97, 0xb0, 0x96, 0x07, 0x1b, 0xf3, 0xad, 0x92, 0xbb, | ||||
| 	0xb0, 0x3a, 0x8e, 0xba, 0xba, 0x08, 0xf7, 0xec, 0xf6, 0x4d, 0x81, 0x92, 0xdd, 0x6a, 0xd9, 0x2e, | ||||
| 	0xf6, 0xde, 0xbb, 0xd3, 0x30, 0x8f, 0x22, 0x3a, 0x61, 0xe2, 0x2b, 0x1e, 0x9c, 0x60, 0xe5, 0xea, | ||||
| 	0x8d, 0x72, 0x60, 0xa5, 0xbc, 0x4f, 0xd9, 0x92, 0x6c, 0x42, 0x7d, 0x76, 0x97, 0x8a, 0x10, 0xf9, | ||||
| 	0x12, 0x56, 0x13, 0xab, 0x2b, 0xdb, 0xa1, 0x27, 0xed, 0x8b, 0xac, 0xb5, 0xcb, 0x90, 0x37, 0x15, | ||||
| 	0x6f, 0xed, 0xc3, 0x5a, 0x99, 0x78, 0xc1, 0x95, 0x87, 0x01, 0x5b, 0x2e, 0xdb, 0x77, 0xf2, 0xb5, | ||||
| 	0xdb, 0x2d, 0x9e, 0xcb, 0x2e, 0x53, 0xff, 0x95, 0xd2, 0xea, 0x19, 0xa5, 0xbf, 0x84, 0x87, 0x17, | ||||
| 	0x28, 0xcd, 0x9b, 0xab, 0x53, 0x6e, 0xae, 0xcd, 0xbc, 0xb3, 0xba, 0x69, 0xd1, 0x2b, 0x8f, 0x7d, | ||||
| 	0x3b, 0x66, 0xd2, 0x9e, 0xb9, 0x3d, 0xaa, 0x28, 0xf9, 0x14, 0x6e, 0x65, 0x2d, 0x55, 0x2a, 0xaa, | ||||
| 	0x98, 0x2f, 0x0c, 0x0b, 0x0b, 0xad, 0xa2, 0x9b, 0x96, 0xdc, 0x45, 0xaa, 0x97, 0x11, 0xd1, 0x60, | ||||
| 	0xb1, 0x41, 0x2d, 0xe7, 0x1d, 0xc8, 0xfd, 0x57, 0xb5, 0xd8, 0x77, 0x77, 0x87, 0x34, 0x19, 0x30, | ||||
| 	0x14, 0x1e, 0x4b, 0xf2, 0x10, 0x9a, 0x25, 0x63, 0xd6, 0x44, 0xa3, 0x68, 0x82, 0xdc, 0x87, 0xfa, | ||||
| 	0x48, 0x07, 0xe9, 0x17, 0x12, 0x02, 0x06, 0xc2, 0xc3, 0x4a, 0x9e, 0xc1, 0x06, 0x97, 0x3e, 0x1d, | ||||
| 	0xab, 0xd4, 0x1f, 0xb0, 0x84, 0x09, 0x8a, 0xd7, 0x6d, 0xbe, 0x23, 0x35, 0x6f, 0x9d, 0xcb, 0x9d, | ||||
| 	0xb1, 0x4a, 0xdf, 0x64, 0x34, 0x2d, 0xf4, 0x10, 0x9a, 0x43, 0x3e, 0x18, 0xfa, 0x23, 0xc1, 0x53, | ||||
| 	0xc1, 0xd5, 0x44, 0xf7, 0xb9, 0x9a, 0xd7, 0x40, 0xf0, 0xc8, 0x62, 0xe4, 0x13, 0x58, 0xcf, 0xfd, | ||||
| 	0x63, 0xca, 0xef, 0x4d, 0xfc, 0xb1, 0xcc, 0x27, 0x8b, 0xec, 0x82, 0xe9, 0x32, 0xf5, 0x6a, 0xf2, | ||||
| 	0x5e, 0x32, 0x41, 0x7e, 0x56, 0x60, 0xd7, 0xb9, 0xeb, 0x47, 0x74, 0x20, 0x9d, 0x65, 0x0c, 0xea, | ||||
| 	0xf3, 0xca, 0x53, 0xef, 0x7a, 0x31, 0xae, 0x7d, 0xa4, 0x91, 0x6d, 0xb8, 0x99, 0x30, 0x16, 0xfa, | ||||
| 	0x99, 0x9c, 0xb0, 0x1b, 0xe8, 0xac, 0x18, 0xd7, 0x91, 0x78, 0x64, 0x68, 0xf9, 0xde, 0x3e, 0x82, | ||||
| 	0x35, 0x9e, 0xcd, 0x74, 0x3e, 0x0f, 0x23, 0xe6, 0xd4, 0x8c, 0xef, 0x5c, 0x9a, 0x1c, 0x77, 0xc2, | ||||
| 	0x88, 0xb9, 0xc7, 0x70, 0x1b, 0xb3, 0x6e, 0x85, 0x4d, 0xda, 0x73, 0x15, 0x1b, 0xb0, 0x5c, 0x3a, | ||||
| 	0x1d, 0x76, 0x75, 0x69, 0xae, 0xdd, 0x3f, 0xd6, 0x8b, 0x9b, 0x79, 0x54, 0xdc, 0x27, 0x3d, 0x3b, | ||||
| 	0xe1, 0xb6, 0xda, 0xb0, 0x8d, 0xe6, 0xba, 0xc1, 0x4c, 0xb4, 0xcf, 0xcf, 0xde, 0x62, 0xf7, 0xda, | ||||
| 	0xf3, 0x95, 0x9d, 0xbd, 0xc6, 0x5a, 0x7f, 0x86, 0xfc, 0x06, 0xb9, 0xe8, 0xe2, 0x9d, 0x39, 0x50, | ||||
| 	0xd5, 0x39, 0x07, 0xea, 0x27, 0x40, 0x06, 0x38, 0x2d, 0xe8, 0xb0, 0x42, 0x9f, 0x8e, 0x46, 0x3e, | ||||
| 	0x0f, 0xed, 0xa5, 0x78, 0x0d, 0x29, 0xba, 0xa6, 0xc2, 0x9d, 0xd1, 0xa8, 0xa3, 0x27, 0x25, 0xcd, | ||||
| 	0x6c, 0xe7, 0x2e, 0x3e, 0xb2, 0x17, 0x62, 0x03, 0x51, 0x33, 0x6d, 0x75, 0x46, 0x78, 0xcb, 0x16, | ||||
| 	0xb9, 0x46, 0xa9, 0x30, 0x57, 0x61, 0xd3, 0x5b, 0x9b, 0xf2, 0x1d, 0xa5, 0x42, 0xdf, 0xb2, 0xe7, | ||||
| 	0x9e, 0x91, 0x79, 0x07, 0xe4, 0x39, 0x38, 0x69, 0x12, 0xf1, 0x04, 0x75, 0x4b, 0xc9, 0xd3, 0xc4, | ||||
| 	0xe7, 0x89, 0x54, 0x34, 0x09, 0x98, 0xd4, 0x67, 0xa4, 0xe9, 0x6d, 0x18, 0x7a, 0xd7, 0x90, 0x3b, | ||||
| 	0x19, 0xf5, 0xbc, 0xc3, 0x0b, 0xe7, 0x1c, 0xde, 0x33, 0x5b, 0x7f, 0x6d, 0xa6, 0xcc, 0x3e, 0x06, | ||||
| 	0xf8, 0x76, 0xcc, 0xc4, 0xc4, 0x44, 0x77, 0x43, 0xdb, 0x5e, 0xd5, 0x88, 0x0e, 0xec, 0x87, 0xd3, | ||||
| 	0x91, 0x52, 0xa6, 0x63, 0x11, 0x30, 0xe7, 0xb6, 0xde, 0x9c, 0xa6, 0x45, 0xbb, 0x1a, 0x44, 0x33, | ||||
| 	0xf4, 0x94, 0x2a, 0x2a, 0xfc, 0x21, 0x95, 0x43, 0xe7, 0xbe, 0x1e, 0x74, 0xc1, 0x40, 0x6f, 0xa9, | ||||
| 	0x1c, 0x22, 0x43, 0x44, 0xa5, 0xf2, 0xa3, 0x74, 0x90, 0xf6, 0xfb, 0xce, 0x27, 0xda, 0x0e, 0x20, | ||||
| 	0x74, 0xa0, 0x11, 0xf4, 0x23, 0x67, 0x48, 0x9c, 0xb6, 0xf1, 0x23, 0xa3, 0x27, 0xb8, 0x15, 0x9a, | ||||
| 	0x2c, 0x19, 0x4b, 0x7c, 0x93, 0x1a, 0xe7, 0xa7, 0x66, 0x2b, 0x10, 0xef, 0x32, 0x96, 0x7c, 0xad, | ||||
| 	0x51, 0x72, 0x07, 0x56, 0x83, 0x88, 0x26, 0xbe, 0xa0, 0xc9, 0x89, 0xb3, 0xad, 0x59, 0x6a, 0x08, | ||||
| 	0x78, 0x34, 0x39, 0x41, 0xa2, 0xde, 0x51, 0x9d, 0x8c, 0xcf, 0x4c, 0x13, 0x46, 0x40, 0xa7, 0x62, | ||||
| 	0x03, 0x96, 0xf1, 0x9b, 0x87, 0xce, 0x73, 0x1d, 0xa3, 0x5d, 0xe5, 0x87, 0x25, 0xa4, 0x8a, 0xfa, | ||||
| 	0xbd, 0x28, 0xed, 0x39, 0x2f, 0x74, 0x7c, 0xfa, 0xb0, 0x60, 0x7b, 0x7d, 0x15, 0xa5, 0x3d, 0xb2, | ||||
| 	0x67, 0xed, 0x22, 0x97, 0xf3, 0x72, 0xb3, 0xb2, 0x55, 0xdf, 0x7e, 0x7c, 0x71, 0x1d, 0xb4, 0x77, | ||||
| 	0x23, 0x9a, 0xa0, 0xbc, 0x71, 0x50, 0x37, 0xea, 0xdb, 0xa0, 0xbf, 0x7d, 0x45, 0x07, 0xce, 0x8e, | ||||
| 	0xf6, 0x6f, 0x05, 0xd7, 0xc7, 0x74, 0x40, 0xf6, 0xa1, 0x29, 0x78, 0x80, 0xbd, 0x8d, 0x49, 0x96, | ||||
| 	0x04, 0xcc, 0x79, 0xa3, 0x8b, 0xed, 0xc1, 0x25, 0x46, 0xde, 0x7d, 0xf0, 0x1a, 0x28, 0x77, 0x64, | ||||
| 	0xc5, 0xb0, 0xa2, 0x7b, 0x22, 0xa5, 0x61, 0x80, 0xf9, 0xe4, 0xa1, 0xf3, 0xd6, 0xbc, 0x86, 0x72, | ||||
| 	0xac, 0x13, 0x12, 0x17, 0x9a, 0x3a, 0xe2, 0x28, 0xed, 0xf5, 0x26, 0xc8, 0xd3, 0x31, 0x3c, 0x08, | ||||
| 	0x1e, 0x20, 0xd6, 0x09, 0xc9, 0x4b, 0xb8, 0xfb, 0x1d, 0x55, 0xc1, 0x90, 0x27, 0x03, 0x7f, 0xaa, | ||||
| 	0xcf, 0x3e, 0x1b, 0x78, 0xe8, 0x7c, 0xa9, 0x53, 0xdf, 0xca, 0x78, 0x5e, 0x65, 0x2c, 0x3b, 0x19, | ||||
| 	0x07, 0x16, 0xc1, 0x3c, 0x0d, 0xa3, 0x11, 0x0f, 0x9d, 0x77, 0xa6, 0x08, 0x66, 0xa5, 0x91, 0x4a, | ||||
| 	0x5e, 0x40, 0x6b, 0x8e, 0xe4, 0x29, 0x67, 0xdf, 0x31, 0x21, 0x9d, 0x03, 0x2d, 0xeb, 0xcc, 0xc8, | ||||
| 	0x7e, 0x30, 0xf4, 0x73, 0xec, 0x2a, 0xae, 0x22, 0xe6, 0x1c, 0xea, 0x9c, 0xcf, 0xda, 0x3d, 0x46, | ||||
| 	0x6a, 0xeb, 0x00, 0x6a, 0xd9, 0x9e, 0x91, 0xbb, 0x00, 0xe9, 0x60, 0x90, 0xf5, 0x19, 0xd3, 0x16, | ||||
| 	0x6b, 0xe9, 0x60, 0x60, 0x1a, 0x8c, 0x0b, 0xfa, 0xe1, 0xe8, 0x0f, 0x70, 0x4c, 0x42, 0x06, 0x6c, | ||||
| 	0x59, 0x8b, 0x5e, 0x1d, 0x41, 0x33, 0x3a, 0x85, 0xad, 0x27, 0x50, 0x7d, 0xf7, 0x81, 0x7c, 0x04, | ||||
| 	0x0b, 0x27, 0x6c, 0xa2, 0x15, 0xac, 0x7a, 0xf8, 0x49, 0x6e, 0xc0, 0xd2, 0x29, 0x8d, 0xc6, 0x59, | ||||
| 	0xa3, 0x36, 0x0b, 0x77, 0x6f, 0x76, 0x1e, 0x3e, 0x12, 0x69, 0x9f, 0x47, 0xac, 0x93, 0xf4, 0xd3, | ||||
| 	0x62, 0xa1, 0x9a, 0xbe, 0x69, 0xbb, 0x68, 0x33, 0x7f, 0xe5, 0x22, 0xe8, 0xfe, 0xa5, 0x5a, 0x9c, | ||||
| 	0x34, 0x66, 0xd4, 0x5c, 0xed, 0x19, 0x37, 0x6b, 0xab, 0x3a, 0xc7, 0x16, 0x1e, 0x34, 0x7c, 0xbe, | ||||
| 	0xfa, 0x81, 0x60, 0x78, 0x41, 0xdb, 0x5e, 0x5c, 0x47, 0x6c, 0xd7, 0x40, 0x58, 0x8f, 0x82, 0xd1, | ||||
| 	0xc8, 0xd4, 0xe3, 0xa2, 0xa9, 0x47, 0x04, 0x74, 0x3d, 0x62, 0x25, 0x73, 0x35, 0x31, 0xc4, 0x25, | ||||
| 	0x43, 0x44, 0x20, 0xeb, 0x5b, 0xa6, 0xd3, 0x6a, 0xea, 0xb2, 0xa6, 0xae, 0x6a, 0x44, 0x93, 0x1f, | ||||
| 	0x40, 0x43, 0x1f, 0x33, 0x61, 0xc5, 0x57, 0x34, 0x43, 0xdd, 0x62, 0x9a, 0xa5, 0x05, 0xb5, 0x21, | ||||
| 	0xa3, 0xa1, 0x6e, 0x25, 0x35, 0xa3, 0x3d, 0x5b, 0xeb, 0x29, 0x6e, 0x1c, 0xc7, 0x54, 0x4c, 0x9c, | ||||
| 	0x55, 0x53, 0x85, 0x76, 0xe9, 0xfe, 0xa6, 0x38, 0xee, 0x9a, 0x30, 0x8a, 0xb3, 0xfd, 0x05, 0xf3, | ||||
| 	0xdf, 0x5d, 0x58, 0xd5, 0xa7, 0xa1, 0x70, 0x07, 0x4f, 0x01, 0xf2, 0x18, 0xae, 0x95, 0x73, 0x6a, | ||||
| 	0x06, 0xde, 0x65, 0x6f, 0xad, 0x94, 0x54, 0xe9, 0x7e, 0x03, 0x8f, 0x2e, 0x72, 0xe0, 0xf2, 0x59, | ||||
| 	0x11, 0x29, 0xda, 0xae, 0x3d, 0x95, 0x4b, 0x5e, 0xb6, 0x74, 0xbd, 0x62, 0x70, 0x7b, 0x2c, 0x62, | ||||
| 	0x57, 0x0e, 0xee, 0x7c, 0x9d, 0x2f, 0x8b, 0xfe, 0xce, 0xea, 0xbc, 0xc2, 0x6c, 0xfb, 0xd7, 0xd2, | ||||
| 	0x13, 0xe3, 0x90, 0x26, 0x74, 0x30, 0xe3, 0x56, 0x66, 0xbc, 0x52, 0x32, 0x7e, 0x49, 0xce, 0xb7, | ||||
| 	0xe1, 0xe6, 0x99, 0x9c, 0xe7, 0xff, 0x1a, 0x30, 0xf3, 0xeb, 0xe5, 0xcc, 0x9b, 0x9f, 0x13, 0x9f, | ||||
| 	0xc2, 0xad, 0xb3, 0x32, 0xfa, 0x3d, 0xc3, 0x42, 0xfd, 0xea, 0x5b, 0xf6, 0x6e, 0x96, 0xa5, 0xcc, | ||||
| 	0x1f, 0x89, 0x33, 0x69, 0x98, 0x8d, 0xe1, 0x0a, 0x69, 0xf8, 0x15, 0xb4, 0xe6, 0xfc, 0x78, 0x39, | ||||
| 	0x4e, 0x2f, 0xcb, 0xc1, 0x26, 0x64, 0x7f, 0xba, 0xf4, 0xa4, 0x50, 0x2d, 0xfd, 0xfc, 0x42, 0xc8, | ||||
| 	0xfd, 0x02, 0xdc, 0xf3, 0x35, 0x5f, 0xc1, 0xb3, 0x5f, 0xc3, 0xfd, 0xf9, 0xbf, 0x60, 0xf6, 0x45, | ||||
| 	0x1a, 0xff, 0xef, 0xee, 0xed, 0xc2, 0xe3, 0x4b, 0xd4, 0x5f, 0xc1, 0xc7, 0x3b, 0x66, 0x70, 0x36, | ||||
| 	0x4a, 0xde, 0x30, 0xf5, 0x3a, 0x4e, 0x15, 0x0f, 0xd2, 0x04, 0x5f, 0x8d, 0xee, 0x3f, 0x17, 0x8b, | ||||
| 	0xf3, 0x6f, 0x91, 0x44, 0xbe, 0x80, 0x55, 0x66, 0xd7, 0x38, 0xfc, 0xe2, 0x8d, 0xbb, 0xd9, 0x9e, | ||||
| 	0xcf, 0xdb, 0xce, 0x16, 0xde, 0x54, 0x84, 0xfc, 0x02, 0x6a, 0x52, 0xf1, 0xe0, 0x04, 0x2f, 0x26, | ||||
| 	0x33, 0x1d, 0xdf, 0x3f, 0x4f, 0xbc, 0x6b, 0xf8, 0xbc, 0x5c, 0x00, 0x27, 0x6b, 0xd6, 0xef, 0xb3, | ||||
| 	0x40, 0x65, 0xaf, 0xdf, 0x7b, 0xe7, 0x9a, 0xd6, 0x6c, 0x5e, 0xc6, 0xde, 0xfa, 0x43, 0x05, 0x6a, | ||||
| 	0x19, 0x03, 0x21, 0xb0, 0xa8, 0x0b, 0xc0, 0xdc, 0x31, 0xfa, 0x1b, 0x2f, 0x19, 0xf3, 0x27, 0xc8, | ||||
| 	0x94, 0xab, 0x59, 0xe0, 0xa8, 0xa3, 0x5b, 0xb6, 0x9e, 0xb5, 0xc6, 0x32, 0x6f, 0xda, 0xba, 0x91, | ||||
| 	0x1f, 0x50, 0xa9, 0xde, 0x4b, 0xd3, 0xb5, 0xc7, 0x92, 0x95, 0xfe, 0x24, 0xd5, 0xc6, 0x92, 0x99, | ||||
| 	0x1f, 0x48, 0x0f, 0xa1, 0xa9, 0x55, 0x08, 0x16, 0x30, 0x8e, 0x65, 0xb1, 0x34, 0xd5, 0xe0, 0x59, | ||||
| 	0xac, 0x95, 0xc0, 0x8a, 0x8d, 0xf6, 0x3f, 0x70, 0x6e, 0x46, 0xf3, 0xc2, 0xac, 0x66, 0x14, 0x35, | ||||
| 	0x13, 0x84, 0xf1, 0xcb, 0x2c, 0x5a, 0xbf, 0xaf, 0xc0, 0xb2, 0x49, 0xd1, 0xff, 0xdb, 0xde, 0x03, | ||||
| 	0x68, 0xf0, 0xa4, 0xcf, 0x13, 0xae, 0x18, 0x26, 0xcc, 0x3e, 0x38, 0xeb, 0x19, 0xf6, 0x5e, 0xb2, | ||||
| 	0xa9, 0x4b, 0x4b, 0x05, 0x97, 0x5e, 0x2d, 0xbd, 0xad, 0xfc, 0xb6, 0xf2, 0x83, 0x7f, 0x07, 0x00, | ||||
| 	0x00, 0xff, 0xff, 0xc5, 0x50, 0x67, 0x9b, 0xd4, 0x17, 0x00, 0x00, | ||||
| } | ||||
|   | ||||
							
								
								
									
										680
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_login.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										680
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_server_login.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -52,7 +52,7 @@ func (m *CMsgClientHeartBeat) XXX_DiscardUnknown() { | ||||
| var xxx_messageInfo_CMsgClientHeartBeat proto.InternalMessageInfo | ||||
|  | ||||
| type CMsgClientServerTimestampRequest struct { | ||||
| 	ClientRequestTimestamp *uint64  `protobuf:"varint,1,opt,name=client_request_timestamp" json:"client_request_timestamp,omitempty"` | ||||
| 	ClientRequestTimestamp *uint64  `protobuf:"varint,1,opt,name=client_request_timestamp,json=clientRequestTimestamp" json:"client_request_timestamp,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral   struct{} `json:"-"` | ||||
| 	XXX_unrecognized       []byte   `json:"-"` | ||||
| 	XXX_sizecache          int32    `json:"-"` | ||||
| @@ -91,8 +91,8 @@ func (m *CMsgClientServerTimestampRequest) GetClientRequestTimestamp() uint64 { | ||||
| } | ||||
|  | ||||
| type CMsgClientServerTimestampResponse struct { | ||||
| 	ClientRequestTimestamp *uint64  `protobuf:"varint,1,opt,name=client_request_timestamp" json:"client_request_timestamp,omitempty"` | ||||
| 	ServerTimestampMs      *uint64  `protobuf:"varint,2,opt,name=server_timestamp_ms" json:"server_timestamp_ms,omitempty"` | ||||
| 	ClientRequestTimestamp *uint64  `protobuf:"varint,1,opt,name=client_request_timestamp,json=clientRequestTimestamp" json:"client_request_timestamp,omitempty"` | ||||
| 	ServerTimestampMs      *uint64  `protobuf:"varint,2,opt,name=server_timestamp_ms,json=serverTimestampMs" json:"server_timestamp_ms,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral   struct{} `json:"-"` | ||||
| 	XXX_unrecognized       []byte   `json:"-"` | ||||
| 	XXX_sizecache          int32    `json:"-"` | ||||
| @@ -137,66 +137,141 @@ func (m *CMsgClientServerTimestampResponse) GetServerTimestampMs() uint64 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| type CMsgClientSecret struct { | ||||
| 	Version              *uint32  `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` | ||||
| 	Appid                *uint32  `protobuf:"varint,2,opt,name=appid" json:"appid,omitempty"` | ||||
| 	Deviceid             *uint32  `protobuf:"varint,3,opt,name=deviceid" json:"deviceid,omitempty"` | ||||
| 	Nonce                *uint64  `protobuf:"fixed64,4,opt,name=nonce" json:"nonce,omitempty"` | ||||
| 	Hmac                 []byte   `protobuf:"bytes,5,opt,name=hmac" json:"hmac,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientSecret) Reset()         { *m = CMsgClientSecret{} } | ||||
| func (m *CMsgClientSecret) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientSecret) ProtoMessage()    {} | ||||
| func (*CMsgClientSecret) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{3} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientSecret) XXX_Unmarshal(b []byte) error { | ||||
| 	return xxx_messageInfo_CMsgClientSecret.Unmarshal(m, b) | ||||
| } | ||||
| func (m *CMsgClientSecret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	return xxx_messageInfo_CMsgClientSecret.Marshal(b, m, deterministic) | ||||
| } | ||||
| func (m *CMsgClientSecret) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_CMsgClientSecret.Merge(m, src) | ||||
| } | ||||
| func (m *CMsgClientSecret) XXX_Size() int { | ||||
| 	return xxx_messageInfo_CMsgClientSecret.Size(m) | ||||
| } | ||||
| func (m *CMsgClientSecret) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_CMsgClientSecret.DiscardUnknown(m) | ||||
| } | ||||
|  | ||||
| var xxx_messageInfo_CMsgClientSecret proto.InternalMessageInfo | ||||
|  | ||||
| func (m *CMsgClientSecret) GetVersion() uint32 { | ||||
| 	if m != nil && m.Version != nil { | ||||
| 		return *m.Version | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientSecret) GetAppid() uint32 { | ||||
| 	if m != nil && m.Appid != nil { | ||||
| 		return *m.Appid | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientSecret) GetDeviceid() uint32 { | ||||
| 	if m != nil && m.Deviceid != nil { | ||||
| 		return *m.Deviceid | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientSecret) GetNonce() uint64 { | ||||
| 	if m != nil && m.Nonce != nil { | ||||
| 		return *m.Nonce | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientSecret) GetHmac() []byte { | ||||
| 	if m != nil { | ||||
| 		return m.Hmac | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type CMsgClientLogon struct { | ||||
| 	ProtocolVersion                   *uint32  `protobuf:"varint,1,opt,name=protocol_version" json:"protocol_version,omitempty"` | ||||
| 	ObfustucatedPrivateIp             *uint32  `protobuf:"varint,2,opt,name=obfustucated_private_ip" json:"obfustucated_private_ip,omitempty"` | ||||
| 	CellId                            *uint32  `protobuf:"varint,3,opt,name=cell_id" json:"cell_id,omitempty"` | ||||
| 	LastSessionId                     *uint32  `protobuf:"varint,4,opt,name=last_session_id" json:"last_session_id,omitempty"` | ||||
| 	ClientPackageVersion              *uint32  `protobuf:"varint,5,opt,name=client_package_version" json:"client_package_version,omitempty"` | ||||
| 	ClientLanguage                    *string  `protobuf:"bytes,6,opt,name=client_language" json:"client_language,omitempty"` | ||||
| 	ClientOsType                      *uint32  `protobuf:"varint,7,opt,name=client_os_type" json:"client_os_type,omitempty"` | ||||
| 	ShouldRememberPassword            *bool    `protobuf:"varint,8,opt,name=should_remember_password,def=0" json:"should_remember_password,omitempty"` | ||||
| 	WineVersion                       *string  `protobuf:"bytes,9,opt,name=wine_version" json:"wine_version,omitempty"` | ||||
| 	PingMsFromCellSearch              *uint32  `protobuf:"varint,10,opt,name=ping_ms_from_cell_search" json:"ping_ms_from_cell_search,omitempty"` | ||||
| 	PublicIp                          *uint32  `protobuf:"varint,20,opt,name=public_ip" json:"public_ip,omitempty"` | ||||
| 	QosLevel                          *uint32  `protobuf:"varint,21,opt,name=qos_level" json:"qos_level,omitempty"` | ||||
| 	ClientSuppliedSteamId             *uint64  `protobuf:"fixed64,22,opt,name=client_supplied_steam_id" json:"client_supplied_steam_id,omitempty"` | ||||
| 	MachineId                         []byte   `protobuf:"bytes,30,opt,name=machine_id" json:"machine_id,omitempty"` | ||||
| 	LauncherType                      *uint32  `protobuf:"varint,31,opt,name=launcher_type,def=0" json:"launcher_type,omitempty"` | ||||
| 	UiMode                            *uint32  `protobuf:"varint,32,opt,name=ui_mode,def=0" json:"ui_mode,omitempty"` | ||||
| 	ChatMode                          *uint32  `protobuf:"varint,33,opt,name=chat_mode,def=0" json:"chat_mode,omitempty"` | ||||
| 	Steam2AuthTicket                  []byte   `protobuf:"bytes,41,opt,name=steam2_auth_ticket" json:"steam2_auth_ticket,omitempty"` | ||||
| 	EmailAddress                      *string  `protobuf:"bytes,42,opt,name=email_address" json:"email_address,omitempty"` | ||||
| 	Rtime32AccountCreation            *uint32  `protobuf:"fixed32,43,opt,name=rtime32_account_creation" json:"rtime32_account_creation,omitempty"` | ||||
| 	AccountName                       *string  `protobuf:"bytes,50,opt,name=account_name" json:"account_name,omitempty"` | ||||
| 	Password                          *string  `protobuf:"bytes,51,opt,name=password" json:"password,omitempty"` | ||||
| 	GameServerToken                   *string  `protobuf:"bytes,52,opt,name=game_server_token" json:"game_server_token,omitempty"` | ||||
| 	LoginKey                          *string  `protobuf:"bytes,60,opt,name=login_key" json:"login_key,omitempty"` | ||||
| 	WasConvertedDeprecatedMsg         *bool    `protobuf:"varint,70,opt,name=was_converted_deprecated_msg,def=0" json:"was_converted_deprecated_msg,omitempty"` | ||||
| 	AnonUserTargetAccountName         *string  `protobuf:"bytes,80,opt,name=anon_user_target_account_name" json:"anon_user_target_account_name,omitempty"` | ||||
| 	ResolvedUserSteamId               *uint64  `protobuf:"fixed64,81,opt,name=resolved_user_steam_id" json:"resolved_user_steam_id,omitempty"` | ||||
| 	EresultSentryfile                 *int32   `protobuf:"varint,82,opt,name=eresult_sentryfile" json:"eresult_sentryfile,omitempty"` | ||||
| 	ShaSentryfile                     []byte   `protobuf:"bytes,83,opt,name=sha_sentryfile" json:"sha_sentryfile,omitempty"` | ||||
| 	AuthCode                          *string  `protobuf:"bytes,84,opt,name=auth_code" json:"auth_code,omitempty"` | ||||
| 	OtpType                           *int32   `protobuf:"varint,85,opt,name=otp_type" json:"otp_type,omitempty"` | ||||
| 	OtpValue                          *uint32  `protobuf:"varint,86,opt,name=otp_value" json:"otp_value,omitempty"` | ||||
| 	OtpIdentifier                     *string  `protobuf:"bytes,87,opt,name=otp_identifier" json:"otp_identifier,omitempty"` | ||||
| 	Steam2TicketRequest               *bool    `protobuf:"varint,88,opt,name=steam2_ticket_request" json:"steam2_ticket_request,omitempty"` | ||||
| 	SonyPsnTicket                     []byte   `protobuf:"bytes,90,opt,name=sony_psn_ticket" json:"sony_psn_ticket,omitempty"` | ||||
| 	SonyPsnServiceId                  *string  `protobuf:"bytes,91,opt,name=sony_psn_service_id" json:"sony_psn_service_id,omitempty"` | ||||
| 	CreateNewPsnLinkedAccountIfNeeded *bool    `protobuf:"varint,92,opt,name=create_new_psn_linked_account_if_needed,def=0" json:"create_new_psn_linked_account_if_needed,omitempty"` | ||||
| 	SonyPsnName                       *string  `protobuf:"bytes,93,opt,name=sony_psn_name" json:"sony_psn_name,omitempty"` | ||||
| 	GameServerAppId                   *int32   `protobuf:"varint,94,opt,name=game_server_app_id" json:"game_server_app_id,omitempty"` | ||||
| 	SteamguardDontRememberComputer    *bool    `protobuf:"varint,95,opt,name=steamguard_dont_remember_computer" json:"steamguard_dont_remember_computer,omitempty"` | ||||
| 	MachineName                       *string  `protobuf:"bytes,96,opt,name=machine_name" json:"machine_name,omitempty"` | ||||
| 	MachineNameUserchosen             *string  `protobuf:"bytes,97,opt,name=machine_name_userchosen" json:"machine_name_userchosen,omitempty"` | ||||
| 	CountryOverride                   *string  `protobuf:"bytes,98,opt,name=country_override" json:"country_override,omitempty"` | ||||
| 	IsSteamBox                        *bool    `protobuf:"varint,99,opt,name=is_steam_box" json:"is_steam_box,omitempty"` | ||||
| 	ClientInstanceId                  *uint64  `protobuf:"varint,100,opt,name=client_instance_id" json:"client_instance_id,omitempty"` | ||||
| 	TwoFactorCode                     *string  `protobuf:"bytes,101,opt,name=two_factor_code" json:"two_factor_code,omitempty"` | ||||
| 	SupportsRateLimitResponse         *bool    `protobuf:"varint,102,opt,name=supports_rate_limit_response" json:"supports_rate_limit_response,omitempty"` | ||||
| 	WebLogonNonce                     *string  `protobuf:"bytes,103,opt,name=web_logon_nonce" json:"web_logon_nonce,omitempty"` | ||||
| 	PriorityReason                    *int32   `protobuf:"varint,104,opt,name=priority_reason" json:"priority_reason,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral              struct{} `json:"-"` | ||||
| 	XXX_unrecognized                  []byte   `json:"-"` | ||||
| 	XXX_sizecache                     int32    `json:"-"` | ||||
| 	ProtocolVersion                   *uint32           `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion" json:"protocol_version,omitempty"` | ||||
| 	DeprecatedObfustucatedPrivateIp   *uint32           `protobuf:"varint,2,opt,name=deprecated_obfustucated_private_ip,json=deprecatedObfustucatedPrivateIp" json:"deprecated_obfustucated_private_ip,omitempty"` | ||||
| 	CellId                            *uint32           `protobuf:"varint,3,opt,name=cell_id,json=cellId" json:"cell_id,omitempty"` | ||||
| 	LastSessionId                     *uint32           `protobuf:"varint,4,opt,name=last_session_id,json=lastSessionId" json:"last_session_id,omitempty"` | ||||
| 	ClientPackageVersion              *uint32           `protobuf:"varint,5,opt,name=client_package_version,json=clientPackageVersion" json:"client_package_version,omitempty"` | ||||
| 	ClientLanguage                    *string           `protobuf:"bytes,6,opt,name=client_language,json=clientLanguage" json:"client_language,omitempty"` | ||||
| 	ClientOsType                      *uint32           `protobuf:"varint,7,opt,name=client_os_type,json=clientOsType" json:"client_os_type,omitempty"` | ||||
| 	ShouldRememberPassword            *bool             `protobuf:"varint,8,opt,name=should_remember_password,json=shouldRememberPassword,def=0" json:"should_remember_password,omitempty"` | ||||
| 	WineVersion                       *string           `protobuf:"bytes,9,opt,name=wine_version,json=wineVersion" json:"wine_version,omitempty"` | ||||
| 	Deprecated_10                     *uint32           `protobuf:"varint,10,opt,name=deprecated_10,json=deprecated10" json:"deprecated_10,omitempty"` | ||||
| 	ObfuscatedPrivateIp               *CMsgIPAddress    `protobuf:"bytes,11,opt,name=obfuscated_private_ip,json=obfuscatedPrivateIp" json:"obfuscated_private_ip,omitempty"` | ||||
| 	DeprecatedPublicIp                *uint32           `protobuf:"varint,20,opt,name=deprecated_public_ip,json=deprecatedPublicIp" json:"deprecated_public_ip,omitempty"` | ||||
| 	QosLevel                          *uint32           `protobuf:"varint,21,opt,name=qos_level,json=qosLevel" json:"qos_level,omitempty"` | ||||
| 	ClientSuppliedSteamId             *uint64           `protobuf:"fixed64,22,opt,name=client_supplied_steam_id,json=clientSuppliedSteamId" json:"client_supplied_steam_id,omitempty"` | ||||
| 	PublicIp                          *CMsgIPAddress    `protobuf:"bytes,23,opt,name=public_ip,json=publicIp" json:"public_ip,omitempty"` | ||||
| 	MachineId                         []byte            `protobuf:"bytes,30,opt,name=machine_id,json=machineId" json:"machine_id,omitempty"` | ||||
| 	LauncherType                      *uint32           `protobuf:"varint,31,opt,name=launcher_type,json=launcherType,def=0" json:"launcher_type,omitempty"` | ||||
| 	UiMode                            *uint32           `protobuf:"varint,32,opt,name=ui_mode,json=uiMode,def=0" json:"ui_mode,omitempty"` | ||||
| 	ChatMode                          *uint32           `protobuf:"varint,33,opt,name=chat_mode,json=chatMode,def=0" json:"chat_mode,omitempty"` | ||||
| 	Steam2AuthTicket                  []byte            `protobuf:"bytes,41,opt,name=steam2_auth_ticket,json=steam2AuthTicket" json:"steam2_auth_ticket,omitempty"` | ||||
| 	EmailAddress                      *string           `protobuf:"bytes,42,opt,name=email_address,json=emailAddress" json:"email_address,omitempty"` | ||||
| 	Rtime32AccountCreation            *uint32           `protobuf:"fixed32,43,opt,name=rtime32_account_creation,json=rtime32AccountCreation" json:"rtime32_account_creation,omitempty"` | ||||
| 	AccountName                       *string           `protobuf:"bytes,50,opt,name=account_name,json=accountName" json:"account_name,omitempty"` | ||||
| 	Password                          *string           `protobuf:"bytes,51,opt,name=password" json:"password,omitempty"` | ||||
| 	GameServerToken                   *string           `protobuf:"bytes,52,opt,name=game_server_token,json=gameServerToken" json:"game_server_token,omitempty"` | ||||
| 	LoginKey                          *string           `protobuf:"bytes,60,opt,name=login_key,json=loginKey" json:"login_key,omitempty"` | ||||
| 	WasConvertedDeprecatedMsg         *bool             `protobuf:"varint,70,opt,name=was_converted_deprecated_msg,json=wasConvertedDeprecatedMsg,def=0" json:"was_converted_deprecated_msg,omitempty"` | ||||
| 	AnonUserTargetAccountName         *string           `protobuf:"bytes,80,opt,name=anon_user_target_account_name,json=anonUserTargetAccountName" json:"anon_user_target_account_name,omitempty"` | ||||
| 	ResolvedUserSteamId               *uint64           `protobuf:"fixed64,81,opt,name=resolved_user_steam_id,json=resolvedUserSteamId" json:"resolved_user_steam_id,omitempty"` | ||||
| 	EresultSentryfile                 *int32            `protobuf:"varint,82,opt,name=eresult_sentryfile,json=eresultSentryfile" json:"eresult_sentryfile,omitempty"` | ||||
| 	ShaSentryfile                     []byte            `protobuf:"bytes,83,opt,name=sha_sentryfile,json=shaSentryfile" json:"sha_sentryfile,omitempty"` | ||||
| 	AuthCode                          *string           `protobuf:"bytes,84,opt,name=auth_code,json=authCode" json:"auth_code,omitempty"` | ||||
| 	OtpType                           *int32            `protobuf:"varint,85,opt,name=otp_type,json=otpType" json:"otp_type,omitempty"` | ||||
| 	OtpValue                          *uint32           `protobuf:"varint,86,opt,name=otp_value,json=otpValue" json:"otp_value,omitempty"` | ||||
| 	OtpIdentifier                     *string           `protobuf:"bytes,87,opt,name=otp_identifier,json=otpIdentifier" json:"otp_identifier,omitempty"` | ||||
| 	Steam2TicketRequest               *bool             `protobuf:"varint,88,opt,name=steam2_ticket_request,json=steam2TicketRequest" json:"steam2_ticket_request,omitempty"` | ||||
| 	SonyPsnTicket                     []byte            `protobuf:"bytes,90,opt,name=sony_psn_ticket,json=sonyPsnTicket" json:"sony_psn_ticket,omitempty"` | ||||
| 	SonyPsnServiceId                  *string           `protobuf:"bytes,91,opt,name=sony_psn_service_id,json=sonyPsnServiceId" json:"sony_psn_service_id,omitempty"` | ||||
| 	CreateNewPsnLinkedAccountIfNeeded *bool             `protobuf:"varint,92,opt,name=create_new_psn_linked_account_if_needed,json=createNewPsnLinkedAccountIfNeeded,def=0" json:"create_new_psn_linked_account_if_needed,omitempty"` | ||||
| 	SonyPsnName                       *string           `protobuf:"bytes,93,opt,name=sony_psn_name,json=sonyPsnName" json:"sony_psn_name,omitempty"` | ||||
| 	GameServerAppId                   *int32            `protobuf:"varint,94,opt,name=game_server_app_id,json=gameServerAppId" json:"game_server_app_id,omitempty"` | ||||
| 	SteamguardDontRememberComputer    *bool             `protobuf:"varint,95,opt,name=steamguard_dont_remember_computer,json=steamguardDontRememberComputer" json:"steamguard_dont_remember_computer,omitempty"` | ||||
| 	MachineName                       *string           `protobuf:"bytes,96,opt,name=machine_name,json=machineName" json:"machine_name,omitempty"` | ||||
| 	MachineNameUserchosen             *string           `protobuf:"bytes,97,opt,name=machine_name_userchosen,json=machineNameUserchosen" json:"machine_name_userchosen,omitempty"` | ||||
| 	CountryOverride                   *string           `protobuf:"bytes,98,opt,name=country_override,json=countryOverride" json:"country_override,omitempty"` | ||||
| 	IsSteamBox                        *bool             `protobuf:"varint,99,opt,name=is_steam_box,json=isSteamBox" json:"is_steam_box,omitempty"` | ||||
| 	ClientInstanceId                  *uint64           `protobuf:"varint,100,opt,name=client_instance_id,json=clientInstanceId" json:"client_instance_id,omitempty"` | ||||
| 	TwoFactorCode                     *string           `protobuf:"bytes,101,opt,name=two_factor_code,json=twoFactorCode" json:"two_factor_code,omitempty"` | ||||
| 	SupportsRateLimitResponse         *bool             `protobuf:"varint,102,opt,name=supports_rate_limit_response,json=supportsRateLimitResponse" json:"supports_rate_limit_response,omitempty"` | ||||
| 	WebLogonNonce                     *string           `protobuf:"bytes,103,opt,name=web_logon_nonce,json=webLogonNonce" json:"web_logon_nonce,omitempty"` | ||||
| 	PriorityReason                    *int32            `protobuf:"varint,104,opt,name=priority_reason,json=priorityReason" json:"priority_reason,omitempty"` | ||||
| 	EmbeddedClientSecret              *CMsgClientSecret `protobuf:"bytes,105,opt,name=embedded_client_secret,json=embeddedClientSecret" json:"embedded_client_secret,omitempty"` | ||||
| 	DisablePartnerAutogrants          *bool             `protobuf:"varint,106,opt,name=disable_partner_autogrants,json=disablePartnerAutogrants" json:"disable_partner_autogrants,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral              struct{}          `json:"-"` | ||||
| 	XXX_unrecognized                  []byte            `json:"-"` | ||||
| 	XXX_sizecache                     int32             `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) Reset()         { *m = CMsgClientLogon{} } | ||||
| func (m *CMsgClientLogon) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientLogon) ProtoMessage()    {} | ||||
| func (*CMsgClientLogon) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{3} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{4} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) XXX_Unmarshal(b []byte) error { | ||||
| @@ -231,9 +306,9 @@ func (m *CMsgClientLogon) GetProtocolVersion() uint32 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetObfustucatedPrivateIp() uint32 { | ||||
| 	if m != nil && m.ObfustucatedPrivateIp != nil { | ||||
| 		return *m.ObfustucatedPrivateIp | ||||
| func (m *CMsgClientLogon) GetDeprecatedObfustucatedPrivateIp() uint32 { | ||||
| 	if m != nil && m.DeprecatedObfustucatedPrivateIp != nil { | ||||
| 		return *m.DeprecatedObfustucatedPrivateIp | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| @@ -287,16 +362,23 @@ func (m *CMsgClientLogon) GetWineVersion() string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetPingMsFromCellSearch() uint32 { | ||||
| 	if m != nil && m.PingMsFromCellSearch != nil { | ||||
| 		return *m.PingMsFromCellSearch | ||||
| func (m *CMsgClientLogon) GetDeprecated_10() uint32 { | ||||
| 	if m != nil && m.Deprecated_10 != nil { | ||||
| 		return *m.Deprecated_10 | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetPublicIp() uint32 { | ||||
| 	if m != nil && m.PublicIp != nil { | ||||
| 		return *m.PublicIp | ||||
| func (m *CMsgClientLogon) GetObfuscatedPrivateIp() *CMsgIPAddress { | ||||
| 	if m != nil { | ||||
| 		return m.ObfuscatedPrivateIp | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetDeprecatedPublicIp() uint32 { | ||||
| 	if m != nil && m.DeprecatedPublicIp != nil { | ||||
| 		return *m.DeprecatedPublicIp | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| @@ -315,6 +397,13 @@ func (m *CMsgClientLogon) GetClientSuppliedSteamId() uint64 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetPublicIp() *CMsgIPAddress { | ||||
| 	if m != nil { | ||||
| 		return m.PublicIp | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetMachineId() []byte { | ||||
| 	if m != nil { | ||||
| 		return m.MachineId | ||||
| @@ -567,40 +656,55 @@ func (m *CMsgClientLogon) GetPriorityReason() int32 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetEmbeddedClientSecret() *CMsgClientSecret { | ||||
| 	if m != nil { | ||||
| 		return m.EmbeddedClientSecret | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogon) GetDisablePartnerAutogrants() bool { | ||||
| 	if m != nil && m.DisablePartnerAutogrants != nil { | ||||
| 		return *m.DisablePartnerAutogrants | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| type CMsgClientLogonResponse struct { | ||||
| 	Eresult                     *int32   `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||
| 	OutOfGameHeartbeatSeconds   *int32   `protobuf:"varint,2,opt,name=out_of_game_heartbeat_seconds" json:"out_of_game_heartbeat_seconds,omitempty"` | ||||
| 	InGameHeartbeatSeconds      *int32   `protobuf:"varint,3,opt,name=in_game_heartbeat_seconds" json:"in_game_heartbeat_seconds,omitempty"` | ||||
| 	PublicIp                    *uint32  `protobuf:"varint,4,opt,name=public_ip" json:"public_ip,omitempty"` | ||||
| 	Rtime32ServerTime           *uint32  `protobuf:"fixed32,5,opt,name=rtime32_server_time" json:"rtime32_server_time,omitempty"` | ||||
| 	AccountFlags                *uint32  `protobuf:"varint,6,opt,name=account_flags" json:"account_flags,omitempty"` | ||||
| 	CellId                      *uint32  `protobuf:"varint,7,opt,name=cell_id" json:"cell_id,omitempty"` | ||||
| 	EmailDomain                 *string  `protobuf:"bytes,8,opt,name=email_domain" json:"email_domain,omitempty"` | ||||
| 	Steam2Ticket                []byte   `protobuf:"bytes,9,opt,name=steam2_ticket" json:"steam2_ticket,omitempty"` | ||||
| 	EresultExtended             *int32   `protobuf:"varint,10,opt,name=eresult_extended" json:"eresult_extended,omitempty"` | ||||
| 	WebapiAuthenticateUserNonce *string  `protobuf:"bytes,11,opt,name=webapi_authenticate_user_nonce" json:"webapi_authenticate_user_nonce,omitempty"` | ||||
| 	CellIdPingThreshold         *uint32  `protobuf:"varint,12,opt,name=cell_id_ping_threshold" json:"cell_id_ping_threshold,omitempty"` | ||||
| 	UsePics                     *bool    `protobuf:"varint,13,opt,name=use_pics" json:"use_pics,omitempty"` | ||||
| 	VanityUrl                   *string  `protobuf:"bytes,14,opt,name=vanity_url" json:"vanity_url,omitempty"` | ||||
| 	ClientSuppliedSteamid       *uint64  `protobuf:"fixed64,20,opt,name=client_supplied_steamid" json:"client_supplied_steamid,omitempty"` | ||||
| 	IpCountryCode               *string  `protobuf:"bytes,21,opt,name=ip_country_code" json:"ip_country_code,omitempty"` | ||||
| 	ParentalSettings            []byte   `protobuf:"bytes,22,opt,name=parental_settings" json:"parental_settings,omitempty"` | ||||
| 	ParentalSettingSignature    []byte   `protobuf:"bytes,23,opt,name=parental_setting_signature" json:"parental_setting_signature,omitempty"` | ||||
| 	CountLoginfailuresToMigrate *int32   `protobuf:"varint,24,opt,name=count_loginfailures_to_migrate" json:"count_loginfailures_to_migrate,omitempty"` | ||||
| 	CountDisconnectsToMigrate   *int32   `protobuf:"varint,25,opt,name=count_disconnects_to_migrate" json:"count_disconnects_to_migrate,omitempty"` | ||||
| 	OgsDataReportTimeWindow     *int32   `protobuf:"varint,26,opt,name=ogs_data_report_time_window" json:"ogs_data_report_time_window,omitempty"` | ||||
| 	ClientInstanceId            *uint64  `protobuf:"varint,27,opt,name=client_instance_id" json:"client_instance_id,omitempty"` | ||||
| 	ForceClientUpdateCheck      *bool    `protobuf:"varint,28,opt,name=force_client_update_check" json:"force_client_update_check,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral        struct{} `json:"-"` | ||||
| 	XXX_unrecognized            []byte   `json:"-"` | ||||
| 	XXX_sizecache               int32    `json:"-"` | ||||
| 	Eresult                     *int32         `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||
| 	OutOfGameHeartbeatSeconds   *int32         `protobuf:"varint,2,opt,name=out_of_game_heartbeat_seconds,json=outOfGameHeartbeatSeconds" json:"out_of_game_heartbeat_seconds,omitempty"` | ||||
| 	InGameHeartbeatSeconds      *int32         `protobuf:"varint,3,opt,name=in_game_heartbeat_seconds,json=inGameHeartbeatSeconds" json:"in_game_heartbeat_seconds,omitempty"` | ||||
| 	DeprecatedPublicIp          *uint32        `protobuf:"varint,4,opt,name=deprecated_public_ip,json=deprecatedPublicIp" json:"deprecated_public_ip,omitempty"` | ||||
| 	Rtime32ServerTime           *uint32        `protobuf:"fixed32,5,opt,name=rtime32_server_time,json=rtime32ServerTime" json:"rtime32_server_time,omitempty"` | ||||
| 	AccountFlags                *uint32        `protobuf:"varint,6,opt,name=account_flags,json=accountFlags" json:"account_flags,omitempty"` | ||||
| 	CellId                      *uint32        `protobuf:"varint,7,opt,name=cell_id,json=cellId" json:"cell_id,omitempty"` | ||||
| 	EmailDomain                 *string        `protobuf:"bytes,8,opt,name=email_domain,json=emailDomain" json:"email_domain,omitempty"` | ||||
| 	Steam2Ticket                []byte         `protobuf:"bytes,9,opt,name=steam2_ticket,json=steam2Ticket" json:"steam2_ticket,omitempty"` | ||||
| 	EresultExtended             *int32         `protobuf:"varint,10,opt,name=eresult_extended,json=eresultExtended" json:"eresult_extended,omitempty"` | ||||
| 	WebapiAuthenticateUserNonce *string        `protobuf:"bytes,11,opt,name=webapi_authenticate_user_nonce,json=webapiAuthenticateUserNonce" json:"webapi_authenticate_user_nonce,omitempty"` | ||||
| 	CellIdPingThreshold         *uint32        `protobuf:"varint,12,opt,name=cell_id_ping_threshold,json=cellIdPingThreshold" json:"cell_id_ping_threshold,omitempty"` | ||||
| 	DeprecatedUsePics           *bool          `protobuf:"varint,13,opt,name=deprecated_use_pics,json=deprecatedUsePics" json:"deprecated_use_pics,omitempty"` | ||||
| 	VanityUrl                   *string        `protobuf:"bytes,14,opt,name=vanity_url,json=vanityUrl" json:"vanity_url,omitempty"` | ||||
| 	PublicIp                    *CMsgIPAddress `protobuf:"bytes,15,opt,name=public_ip,json=publicIp" json:"public_ip,omitempty"` | ||||
| 	ClientSuppliedSteamid       *uint64        `protobuf:"fixed64,20,opt,name=client_supplied_steamid,json=clientSuppliedSteamid" json:"client_supplied_steamid,omitempty"` | ||||
| 	IpCountryCode               *string        `protobuf:"bytes,21,opt,name=ip_country_code,json=ipCountryCode" json:"ip_country_code,omitempty"` | ||||
| 	ParentalSettings            []byte         `protobuf:"bytes,22,opt,name=parental_settings,json=parentalSettings" json:"parental_settings,omitempty"` | ||||
| 	ParentalSettingSignature    []byte         `protobuf:"bytes,23,opt,name=parental_setting_signature,json=parentalSettingSignature" json:"parental_setting_signature,omitempty"` | ||||
| 	CountLoginfailuresToMigrate *int32         `protobuf:"varint,24,opt,name=count_loginfailures_to_migrate,json=countLoginfailuresToMigrate" json:"count_loginfailures_to_migrate,omitempty"` | ||||
| 	CountDisconnectsToMigrate   *int32         `protobuf:"varint,25,opt,name=count_disconnects_to_migrate,json=countDisconnectsToMigrate" json:"count_disconnects_to_migrate,omitempty"` | ||||
| 	OgsDataReportTimeWindow     *int32         `protobuf:"varint,26,opt,name=ogs_data_report_time_window,json=ogsDataReportTimeWindow" json:"ogs_data_report_time_window,omitempty"` | ||||
| 	ClientInstanceId            *uint64        `protobuf:"varint,27,opt,name=client_instance_id,json=clientInstanceId" json:"client_instance_id,omitempty"` | ||||
| 	ForceClientUpdateCheck      *bool          `protobuf:"varint,28,opt,name=force_client_update_check,json=forceClientUpdateCheck" json:"force_client_update_check,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral        struct{}       `json:"-"` | ||||
| 	XXX_unrecognized            []byte         `json:"-"` | ||||
| 	XXX_sizecache               int32          `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogonResponse) Reset()         { *m = CMsgClientLogonResponse{} } | ||||
| func (m *CMsgClientLogonResponse) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientLogonResponse) ProtoMessage()    {} | ||||
| func (*CMsgClientLogonResponse) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{4} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{5} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogonResponse) XXX_Unmarshal(b []byte) error { | ||||
| @@ -644,9 +748,9 @@ func (m *CMsgClientLogonResponse) GetInGameHeartbeatSeconds() int32 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogonResponse) GetPublicIp() uint32 { | ||||
| 	if m != nil && m.PublicIp != nil { | ||||
| 		return *m.PublicIp | ||||
| func (m *CMsgClientLogonResponse) GetDeprecatedPublicIp() uint32 { | ||||
| 	if m != nil && m.DeprecatedPublicIp != nil { | ||||
| 		return *m.DeprecatedPublicIp | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| @@ -707,9 +811,9 @@ func (m *CMsgClientLogonResponse) GetCellIdPingThreshold() uint32 { | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogonResponse) GetUsePics() bool { | ||||
| 	if m != nil && m.UsePics != nil { | ||||
| 		return *m.UsePics | ||||
| func (m *CMsgClientLogonResponse) GetDeprecatedUsePics() bool { | ||||
| 	if m != nil && m.DeprecatedUsePics != nil { | ||||
| 		return *m.DeprecatedUsePics | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| @@ -721,6 +825,13 @@ func (m *CMsgClientLogonResponse) GetVanityUrl() string { | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogonResponse) GetPublicIp() *CMsgIPAddress { | ||||
| 	if m != nil { | ||||
| 		return m.PublicIp | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogonResponse) GetClientSuppliedSteamid() uint64 { | ||||
| 	if m != nil && m.ClientSuppliedSteamid != nil { | ||||
| 		return *m.ClientSuppliedSteamid | ||||
| @@ -785,7 +896,7 @@ func (m *CMsgClientLogonResponse) GetForceClientUpdateCheck() bool { | ||||
| } | ||||
|  | ||||
| type CMsgClientRequestWebAPIAuthenticateUserNonce struct { | ||||
| 	TokenType            *int32   `protobuf:"varint,1,opt,name=token_type,def=-1" json:"token_type,omitempty"` | ||||
| 	TokenType            *int32   `protobuf:"varint,1,opt,name=token_type,json=tokenType,def=-1" json:"token_type,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -799,7 +910,7 @@ func (m *CMsgClientRequestWebAPIAuthenticateUserNonce) String() string { | ||||
| } | ||||
| func (*CMsgClientRequestWebAPIAuthenticateUserNonce) ProtoMessage() {} | ||||
| func (*CMsgClientRequestWebAPIAuthenticateUserNonce) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{5} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{6} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientRequestWebAPIAuthenticateUserNonce) XXX_Unmarshal(b []byte) error { | ||||
| @@ -831,8 +942,8 @@ func (m *CMsgClientRequestWebAPIAuthenticateUserNonce) GetTokenType() int32 { | ||||
|  | ||||
| type CMsgClientRequestWebAPIAuthenticateUserNonceResponse struct { | ||||
| 	Eresult                     *int32   `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"` | ||||
| 	WebapiAuthenticateUserNonce *string  `protobuf:"bytes,11,opt,name=webapi_authenticate_user_nonce" json:"webapi_authenticate_user_nonce,omitempty"` | ||||
| 	TokenType                   *int32   `protobuf:"varint,3,opt,name=token_type,def=-1" json:"token_type,omitempty"` | ||||
| 	WebapiAuthenticateUserNonce *string  `protobuf:"bytes,11,opt,name=webapi_authenticate_user_nonce,json=webapiAuthenticateUserNonce" json:"webapi_authenticate_user_nonce,omitempty"` | ||||
| 	TokenType                   *int32   `protobuf:"varint,3,opt,name=token_type,json=tokenType,def=-1" json:"token_type,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral        struct{} `json:"-"` | ||||
| 	XXX_unrecognized            []byte   `json:"-"` | ||||
| 	XXX_sizecache               int32    `json:"-"` | ||||
| @@ -846,7 +957,7 @@ func (m *CMsgClientRequestWebAPIAuthenticateUserNonceResponse) String() string { | ||||
| } | ||||
| func (*CMsgClientRequestWebAPIAuthenticateUserNonceResponse) ProtoMessage() {} | ||||
| func (*CMsgClientRequestWebAPIAuthenticateUserNonceResponse) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{6} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{7} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientRequestWebAPIAuthenticateUserNonceResponse) XXX_Unmarshal(b []byte) error { | ||||
| @@ -901,7 +1012,7 @@ func (m *CMsgClientLogOff) Reset()         { *m = CMsgClientLogOff{} } | ||||
| func (m *CMsgClientLogOff) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientLogOff) ProtoMessage()    {} | ||||
| func (*CMsgClientLogOff) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{7} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{8} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLogOff) XXX_Unmarshal(b []byte) error { | ||||
| @@ -933,7 +1044,7 @@ func (m *CMsgClientLoggedOff) Reset()         { *m = CMsgClientLoggedOff{} } | ||||
| func (m *CMsgClientLoggedOff) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientLoggedOff) ProtoMessage()    {} | ||||
| func (*CMsgClientLoggedOff) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{8} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{9} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientLoggedOff) XXX_Unmarshal(b []byte) error { | ||||
| @@ -964,8 +1075,8 @@ func (m *CMsgClientLoggedOff) GetEresult() int32 { | ||||
| } | ||||
|  | ||||
| type CMsgClientNewLoginKey struct { | ||||
| 	UniqueId             *uint32  `protobuf:"varint,1,opt,name=unique_id" json:"unique_id,omitempty"` | ||||
| 	LoginKey             *string  `protobuf:"bytes,2,opt,name=login_key" json:"login_key,omitempty"` | ||||
| 	UniqueId             *uint32  `protobuf:"varint,1,opt,name=unique_id,json=uniqueId" json:"unique_id,omitempty"` | ||||
| 	LoginKey             *string  `protobuf:"bytes,2,opt,name=login_key,json=loginKey" json:"login_key,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -975,7 +1086,7 @@ func (m *CMsgClientNewLoginKey) Reset()         { *m = CMsgClientNewLoginKey{} } | ||||
| func (m *CMsgClientNewLoginKey) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientNewLoginKey) ProtoMessage()    {} | ||||
| func (*CMsgClientNewLoginKey) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{9} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{10} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientNewLoginKey) XXX_Unmarshal(b []byte) error { | ||||
| @@ -1011,7 +1122,7 @@ func (m *CMsgClientNewLoginKey) GetLoginKey() string { | ||||
| } | ||||
|  | ||||
| type CMsgClientNewLoginKeyAccepted struct { | ||||
| 	UniqueId             *uint32  `protobuf:"varint,1,opt,name=unique_id" json:"unique_id,omitempty"` | ||||
| 	UniqueId             *uint32  `protobuf:"varint,1,opt,name=unique_id,json=uniqueId" json:"unique_id,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -1021,7 +1132,7 @@ func (m *CMsgClientNewLoginKeyAccepted) Reset()         { *m = CMsgClientNewLogi | ||||
| func (m *CMsgClientNewLoginKeyAccepted) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientNewLoginKeyAccepted) ProtoMessage()    {} | ||||
| func (*CMsgClientNewLoginKeyAccepted) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{10} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{11} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientNewLoginKeyAccepted) XXX_Unmarshal(b []byte) error { | ||||
| @@ -1050,18 +1161,18 @@ func (m *CMsgClientNewLoginKeyAccepted) GetUniqueId() uint32 { | ||||
| } | ||||
|  | ||||
| type CMsgClientAccountInfo struct { | ||||
| 	PersonaName                     *string  `protobuf:"bytes,1,opt,name=persona_name" json:"persona_name,omitempty"` | ||||
| 	IpCountry                       *string  `protobuf:"bytes,2,opt,name=ip_country" json:"ip_country,omitempty"` | ||||
| 	CountAuthedComputers            *int32   `protobuf:"varint,5,opt,name=count_authed_computers" json:"count_authed_computers,omitempty"` | ||||
| 	AccountFlags                    *uint32  `protobuf:"varint,7,opt,name=account_flags" json:"account_flags,omitempty"` | ||||
| 	FacebookId                      *uint64  `protobuf:"varint,8,opt,name=facebook_id" json:"facebook_id,omitempty"` | ||||
| 	FacebookName                    *string  `protobuf:"bytes,9,opt,name=facebook_name" json:"facebook_name,omitempty"` | ||||
| 	SteamguardNotifyNewmachines     *bool    `protobuf:"varint,14,opt,name=steamguard_notify_newmachines" json:"steamguard_notify_newmachines,omitempty"` | ||||
| 	SteamguardMachineNameUserChosen *string  `protobuf:"bytes,15,opt,name=steamguard_machine_name_user_chosen" json:"steamguard_machine_name_user_chosen,omitempty"` | ||||
| 	IsPhoneVerified                 *bool    `protobuf:"varint,16,opt,name=is_phone_verified" json:"is_phone_verified,omitempty"` | ||||
| 	TwoFactorState                  *uint32  `protobuf:"varint,17,opt,name=two_factor_state" json:"two_factor_state,omitempty"` | ||||
| 	IsPhoneIdentifying              *bool    `protobuf:"varint,18,opt,name=is_phone_identifying" json:"is_phone_identifying,omitempty"` | ||||
| 	IsPhoneNeedingReverify          *bool    `protobuf:"varint,19,opt,name=is_phone_needing_reverify" json:"is_phone_needing_reverify,omitempty"` | ||||
| 	PersonaName                     *string  `protobuf:"bytes,1,opt,name=persona_name,json=personaName" json:"persona_name,omitempty"` | ||||
| 	IpCountry                       *string  `protobuf:"bytes,2,opt,name=ip_country,json=ipCountry" json:"ip_country,omitempty"` | ||||
| 	CountAuthedComputers            *int32   `protobuf:"varint,5,opt,name=count_authed_computers,json=countAuthedComputers" json:"count_authed_computers,omitempty"` | ||||
| 	AccountFlags                    *uint32  `protobuf:"varint,7,opt,name=account_flags,json=accountFlags" json:"account_flags,omitempty"` | ||||
| 	FacebookId                      *uint64  `protobuf:"varint,8,opt,name=facebook_id,json=facebookId" json:"facebook_id,omitempty"` | ||||
| 	FacebookName                    *string  `protobuf:"bytes,9,opt,name=facebook_name,json=facebookName" json:"facebook_name,omitempty"` | ||||
| 	SteamguardNotifyNewmachines     *bool    `protobuf:"varint,14,opt,name=steamguard_notify_newmachines,json=steamguardNotifyNewmachines" json:"steamguard_notify_newmachines,omitempty"` | ||||
| 	SteamguardMachineNameUserChosen *string  `protobuf:"bytes,15,opt,name=steamguard_machine_name_user_chosen,json=steamguardMachineNameUserChosen" json:"steamguard_machine_name_user_chosen,omitempty"` | ||||
| 	IsPhoneVerified                 *bool    `protobuf:"varint,16,opt,name=is_phone_verified,json=isPhoneVerified" json:"is_phone_verified,omitempty"` | ||||
| 	TwoFactorState                  *uint32  `protobuf:"varint,17,opt,name=two_factor_state,json=twoFactorState" json:"two_factor_state,omitempty"` | ||||
| 	IsPhoneIdentifying              *bool    `protobuf:"varint,18,opt,name=is_phone_identifying,json=isPhoneIdentifying" json:"is_phone_identifying,omitempty"` | ||||
| 	IsPhoneNeedingReverify          *bool    `protobuf:"varint,19,opt,name=is_phone_needing_reverify,json=isPhoneNeedingReverify" json:"is_phone_needing_reverify,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral            struct{} `json:"-"` | ||||
| 	XXX_unrecognized                []byte   `json:"-"` | ||||
| 	XXX_sizecache                   int32    `json:"-"` | ||||
| @@ -1071,7 +1182,7 @@ func (m *CMsgClientAccountInfo) Reset()         { *m = CMsgClientAccountInfo{} } | ||||
| func (m *CMsgClientAccountInfo) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientAccountInfo) ProtoMessage()    {} | ||||
| func (*CMsgClientAccountInfo) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{11} | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{12} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientAccountInfo) XXX_Unmarshal(b []byte) error { | ||||
| @@ -1176,10 +1287,89 @@ func (m *CMsgClientAccountInfo) GetIsPhoneNeedingReverify() bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| type CMsgClientChallengeRequest struct { | ||||
| 	Steamid              *uint64  `protobuf:"fixed64,1,opt,name=steamid" json:"steamid,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientChallengeRequest) Reset()         { *m = CMsgClientChallengeRequest{} } | ||||
| func (m *CMsgClientChallengeRequest) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientChallengeRequest) ProtoMessage()    {} | ||||
| func (*CMsgClientChallengeRequest) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{13} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientChallengeRequest) XXX_Unmarshal(b []byte) error { | ||||
| 	return xxx_messageInfo_CMsgClientChallengeRequest.Unmarshal(m, b) | ||||
| } | ||||
| func (m *CMsgClientChallengeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	return xxx_messageInfo_CMsgClientChallengeRequest.Marshal(b, m, deterministic) | ||||
| } | ||||
| func (m *CMsgClientChallengeRequest) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_CMsgClientChallengeRequest.Merge(m, src) | ||||
| } | ||||
| func (m *CMsgClientChallengeRequest) XXX_Size() int { | ||||
| 	return xxx_messageInfo_CMsgClientChallengeRequest.Size(m) | ||||
| } | ||||
| func (m *CMsgClientChallengeRequest) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_CMsgClientChallengeRequest.DiscardUnknown(m) | ||||
| } | ||||
|  | ||||
| var xxx_messageInfo_CMsgClientChallengeRequest proto.InternalMessageInfo | ||||
|  | ||||
| func (m *CMsgClientChallengeRequest) GetSteamid() uint64 { | ||||
| 	if m != nil && m.Steamid != nil { | ||||
| 		return *m.Steamid | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| type CMsgClientChallengeResponse struct { | ||||
| 	Challenge            *uint64  `protobuf:"fixed64,1,opt,name=challenge" json:"challenge,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientChallengeResponse) Reset()         { *m = CMsgClientChallengeResponse{} } | ||||
| func (m *CMsgClientChallengeResponse) String() string { return proto.CompactTextString(m) } | ||||
| func (*CMsgClientChallengeResponse) ProtoMessage()    {} | ||||
| func (*CMsgClientChallengeResponse) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_c98cb07f62c057af, []int{14} | ||||
| } | ||||
|  | ||||
| func (m *CMsgClientChallengeResponse) XXX_Unmarshal(b []byte) error { | ||||
| 	return xxx_messageInfo_CMsgClientChallengeResponse.Unmarshal(m, b) | ||||
| } | ||||
| func (m *CMsgClientChallengeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	return xxx_messageInfo_CMsgClientChallengeResponse.Marshal(b, m, deterministic) | ||||
| } | ||||
| func (m *CMsgClientChallengeResponse) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_CMsgClientChallengeResponse.Merge(m, src) | ||||
| } | ||||
| func (m *CMsgClientChallengeResponse) XXX_Size() int { | ||||
| 	return xxx_messageInfo_CMsgClientChallengeResponse.Size(m) | ||||
| } | ||||
| func (m *CMsgClientChallengeResponse) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_CMsgClientChallengeResponse.DiscardUnknown(m) | ||||
| } | ||||
|  | ||||
| var xxx_messageInfo_CMsgClientChallengeResponse proto.InternalMessageInfo | ||||
|  | ||||
| func (m *CMsgClientChallengeResponse) GetChallenge() uint64 { | ||||
| 	if m != nil && m.Challenge != nil { | ||||
| 		return *m.Challenge | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	proto.RegisterType((*CMsgClientHeartBeat)(nil), "CMsgClientHeartBeat") | ||||
| 	proto.RegisterType((*CMsgClientServerTimestampRequest)(nil), "CMsgClientServerTimestampRequest") | ||||
| 	proto.RegisterType((*CMsgClientServerTimestampResponse)(nil), "CMsgClientServerTimestampResponse") | ||||
| 	proto.RegisterType((*CMsgClientSecret)(nil), "CMsgClientSecret") | ||||
| 	proto.RegisterType((*CMsgClientLogon)(nil), "CMsgClientLogon") | ||||
| 	proto.RegisterType((*CMsgClientLogonResponse)(nil), "CMsgClientLogonResponse") | ||||
| 	proto.RegisterType((*CMsgClientRequestWebAPIAuthenticateUserNonce)(nil), "CMsgClientRequestWebAPIAuthenticateUserNonce") | ||||
| @@ -1189,6 +1379,8 @@ func init() { | ||||
| 	proto.RegisterType((*CMsgClientNewLoginKey)(nil), "CMsgClientNewLoginKey") | ||||
| 	proto.RegisterType((*CMsgClientNewLoginKeyAccepted)(nil), "CMsgClientNewLoginKeyAccepted") | ||||
| 	proto.RegisterType((*CMsgClientAccountInfo)(nil), "CMsgClientAccountInfo") | ||||
| 	proto.RegisterType((*CMsgClientChallengeRequest)(nil), "CMsgClientChallengeRequest") | ||||
| 	proto.RegisterType((*CMsgClientChallengeResponse)(nil), "CMsgClientChallengeResponse") | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| @@ -1196,99 +1388,151 @@ func init() { | ||||
| } | ||||
|  | ||||
| var fileDescriptor_c98cb07f62c057af = []byte{ | ||||
| 	// 1495 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0x5b, 0x73, 0x13, 0x47, | ||||
| 	0x16, 0x5e, 0x01, 0xc2, 0xd6, 0xc1, 0xc6, 0xf2, 0xd8, 0xb2, 0xdb, 0x57, 0x64, 0x71, 0x33, 0xcb, | ||||
| 	0xee, 0xd6, 0xae, 0xa1, 0xf6, 0x81, 0x4a, 0x1e, 0x0c, 0x29, 0x0a, 0x2a, 0x04, 0x08, 0x97, 0x90, | ||||
| 	0xca, 0xad, 0xd3, 0xea, 0x39, 0x33, 0xea, 0xf2, 0x4c, 0xf7, 0xd0, 0xdd, 0x23, 0xa1, 0xb7, 0x3c, | ||||
| 	0xe7, 0xcf, 0xe4, 0x7f, 0xe4, 0xc7, 0xe4, 0x37, 0xa4, 0xfa, 0x8c, 0x46, 0x92, 0xc1, 0x21, 0xc9, | ||||
| 	0xeb, 0xe9, 0xd3, 0xe7, 0xfa, 0x7d, 0x5f, 0x37, 0xdc, 0x70, 0x1e, 0x45, 0x9e, 0xa3, 0x73, 0x22, | ||||
| 	0x45, 0xc7, 0x65, 0xa6, 0x50, 0x7b, 0x87, 0x76, 0x88, 0x96, 0x67, 0x26, 0x55, 0xfa, 0x3f, 0x85, | ||||
| 	0x35, 0xde, 0x6c, 0xb3, 0xd3, 0x7e, 0x7d, 0xe1, 0xb0, 0x3a, 0xe9, 0x75, 0x60, 0xed, 0xc1, 0x17, | ||||
| 	0x2e, 0x7d, 0x40, 0x37, 0x1f, 0xa1, 0xb0, 0xfe, 0x3e, 0x0a, 0xdf, 0xfb, 0x0c, 0xba, 0x33, 0xf3, | ||||
| 	0x4b, 0x0a, 0xf8, 0x4a, 0xe5, 0xe8, 0xbc, 0xc8, 0x8b, 0x17, 0xf8, 0xb6, 0x44, 0xe7, 0xa3, 0x2e, | ||||
| 	0xb0, 0x2a, 0x21, 0xb7, 0x95, 0x85, 0xfb, 0xda, 0x85, 0x35, 0xba, 0x8d, 0xc3, 0x0b, 0xbd, 0x3e, | ||||
| 	0x1c, 0x7c, 0x24, 0x8a, 0x2b, 0x8c, 0x76, 0xf8, 0xe7, 0x61, 0xa2, 0x1d, 0x58, 0x9b, 0xf4, 0x34, | ||||
| 	0x3d, 0xe1, 0xb9, 0x63, 0xe7, 0x28, 0xc7, 0xaf, 0x00, 0x2b, 0xb3, 0x24, 0x4f, 0x4c, 0x6a, 0x74, | ||||
| 	0xc4, 0xa0, 0x4d, 0xdd, 0x49, 0x93, 0xf1, 0x21, 0x5a, 0xa7, 0x8c, 0xa6, 0x50, 0xcb, 0xd1, 0x15, | ||||
| 	0xd8, 0x34, 0xfd, 0xa4, 0x74, 0xbe, 0x94, 0xc2, 0x63, 0xcc, 0x0b, 0xab, 0x86, 0xc2, 0x23, 0x57, | ||||
| 	0x05, 0x85, 0x5b, 0x8e, 0x56, 0x60, 0x41, 0x62, 0x96, 0x71, 0x15, 0xb3, 0xf3, 0x64, 0xd8, 0x84, | ||||
| 	0x95, 0x4c, 0x38, 0xcf, 0x1d, 0xba, 0x10, 0x27, 0x1c, 0x5c, 0xa0, 0x83, 0x7d, 0xd8, 0x98, 0xd4, | ||||
| 	0x5d, 0x08, 0x79, 0x22, 0x52, 0x9c, 0xa6, 0x6a, 0xd6, 0x17, 0x27, 0xe7, 0x99, 0xd0, 0x69, 0x29, | ||||
| 	0x52, 0x64, 0x17, 0xbb, 0x8d, 0xc3, 0x56, 0xb4, 0x01, 0x97, 0x27, 0x07, 0xc6, 0x71, 0x3f, 0x2e, | ||||
| 	0x90, 0x2d, 0xd0, 0x85, 0x9b, 0xc0, 0xdc, 0xc0, 0x94, 0x59, 0xcc, 0x2d, 0xe6, 0x98, 0xf7, 0xd1, | ||||
| 	0xf2, 0x42, 0x38, 0x37, 0x32, 0x36, 0x66, 0x8b, 0xdd, 0xc6, 0xe1, 0xe2, 0xbd, 0x66, 0x22, 0x32, | ||||
| 	0x87, 0xd1, 0x3a, 0x2c, 0x8d, 0x94, 0x9e, 0xe5, 0x6b, 0x51, 0xd8, 0x2e, 0xb0, 0x42, 0xe9, 0x94, | ||||
| 	0xe7, 0x8e, 0x27, 0xd6, 0xe4, 0x9c, 0xda, 0x70, 0x28, 0xac, 0x1c, 0x30, 0xa0, 0x04, 0xab, 0xd0, | ||||
| 	0x2a, 0xca, 0x7e, 0xa6, 0x64, 0x68, 0x77, 0xbd, 0x36, 0xbd, 0x35, 0x8e, 0x67, 0x38, 0xc4, 0x8c, | ||||
| 	0x75, 0xc8, 0x34, 0xdb, 0x87, 0x2b, 0x8b, 0x22, 0x53, 0x18, 0x73, 0x42, 0x4f, 0xe8, 0x7c, 0xa3, | ||||
| 	0xdb, 0x38, 0xbc, 0x18, 0x45, 0x00, 0xb9, 0x90, 0x83, 0x50, 0x82, 0x8a, 0xd9, 0x7e, 0xb7, 0x71, | ||||
| 	0xb8, 0x14, 0x31, 0x58, 0xce, 0x44, 0xa9, 0xe5, 0x20, 0x6c, 0x29, 0xf4, 0x74, 0x25, 0x04, 0xbb, | ||||
| 	0xd7, 0xf8, 0x6f, 0x14, 0xc1, 0x42, 0xa9, 0x78, 0x6e, 0x62, 0x64, 0xdd, 0xda, 0xb6, 0x0e, 0x2d, | ||||
| 	0x39, 0x10, 0xbe, 0xb2, 0x1e, 0xd4, 0xd6, 0x6d, 0x88, 0x28, 0xd3, 0x11, 0x17, 0xa5, 0x1f, 0x70, | ||||
| 	0xaf, 0xe4, 0x09, 0x7a, 0x76, 0x8b, 0xe2, 0x77, 0x60, 0x19, 0x73, 0xa1, 0x32, 0x2e, 0xe2, 0xd8, | ||||
| 	0xa2, 0x73, 0xec, 0x9f, 0x75, 0xd3, 0x36, 0x80, 0xe2, 0xce, 0x11, 0x17, 0x52, 0x9a, 0x52, 0x7b, | ||||
| 	0x2e, 0x2d, 0x0a, 0x1f, 0xc6, 0x72, 0xbb, 0xdb, 0x38, 0x5c, 0x08, 0xc3, 0xaa, 0x4f, 0xb4, 0xc8, | ||||
| 	0x91, 0x1d, 0xd1, 0xbd, 0x36, 0x2c, 0x4e, 0x67, 0x7b, 0x87, 0x2c, 0x5b, 0xb0, 0x9a, 0x8a, 0x1c, | ||||
| 	0x79, 0x8d, 0x34, 0x73, 0x82, 0x9a, 0xdd, 0xa5, 0xa3, 0x55, 0x68, 0x11, 0x99, 0xf8, 0x09, 0x8e, | ||||
| 	0xd9, 0x27, 0x64, 0xba, 0x0d, 0xbb, 0x23, 0xe1, 0xb8, 0x34, 0x7a, 0x88, 0x36, 0x00, 0x29, 0xc6, | ||||
| 	0xc2, 0x62, 0x85, 0xa9, 0xdc, 0xa5, 0xec, 0xe1, 0xfc, 0xbe, 0xae, 0xc3, 0x9e, 0xd0, 0x46, 0xf3, | ||||
| 	0xd2, 0x85, 0xc0, 0xc2, 0xa6, 0xe8, 0xf9, 0xa9, 0x9a, 0x9e, 0x53, 0xcc, 0x7d, 0xd8, 0xb0, 0xe8, | ||||
| 	0x4c, 0x36, 0xc4, 0xb8, 0x72, 0x9d, 0x8e, 0xfd, 0x4b, 0x1a, 0xfb, 0x36, 0x44, 0x68, 0xd1, 0x95, | ||||
| 	0x59, 0x00, 0xa3, 0xf6, 0x76, 0x9c, 0xa8, 0x0c, 0xd9, 0x8b, 0x6e, 0xe3, 0xb0, 0x19, 0x30, 0xe5, | ||||
| 	0x06, 0x62, 0xde, 0xfe, 0x92, 0xc6, 0xb6, 0x0a, 0x2d, 0x9a, 0xa5, 0x0c, 0x83, 0x7e, 0x55, 0xb7, | ||||
| 	0x6e, 0x7c, 0x51, 0x2d, 0xe9, 0x35, 0x5d, 0x5e, 0x85, 0x56, 0xb0, 0x0c, 0x45, 0x56, 0x22, 0xfb, | ||||
| 	0x8a, 0x40, 0xb0, 0x01, 0x97, 0x83, 0x49, 0xc5, 0xa8, 0xbd, 0x4a, 0x14, 0x5a, 0xf6, 0x86, 0x2e, | ||||
| 	0xef, 0x41, 0x67, 0xb2, 0xa2, 0x6a, 0x3b, 0x35, 0x67, 0xd9, 0xd7, 0xa1, 0xe1, 0x80, 0x79, 0x67, | ||||
| 	0xf4, 0x98, 0x17, 0x4e, 0xd7, 0xeb, 0xfb, 0x86, 0xea, 0x08, 0x14, 0xae, 0x0f, 0xc2, 0x84, 0x95, | ||||
| 	0x24, 0xec, 0x7c, 0x4b, 0x41, 0xff, 0x0f, 0x37, 0x69, 0x69, 0xc8, 0x35, 0x8e, 0xc8, 0x25, 0x53, | ||||
| 	0xfa, 0x04, 0xe3, 0xe9, 0x90, 0x54, 0xc2, 0x35, 0x62, 0x8c, 0x31, 0xfb, 0x6e, 0x7e, 0xae, 0x1d, | ||||
| 	0x58, 0x9e, 0x06, 0xa5, 0x39, 0x7e, 0x4f, 0xe1, 0xb6, 0x21, 0x9a, 0xdf, 0xa4, 0x28, 0x42, 0x1f, | ||||
| 	0xec, 0x07, 0x6a, 0xf5, 0x16, 0x1c, 0x50, 0xfd, 0x69, 0x29, 0x6c, 0xcc, 0x63, 0x43, 0xaa, 0x33, | ||||
| 	0x21, 0x9b, 0x34, 0x79, 0x51, 0x7a, 0xb4, 0x8c, 0x53, 0x2f, 0xeb, 0xb0, 0x54, 0xa3, 0x9c, 0x82, | ||||
| 	0xff, 0x48, 0xc1, 0xaf, 0xc0, 0xe6, 0xbc, 0x95, 0x16, 0x25, 0x07, 0xc6, 0xa1, 0x66, 0x82, 0x1c, | ||||
| 	0x18, 0xb4, 0xa9, 0x68, 0x3b, 0xe6, 0x66, 0x88, 0xd6, 0xaa, 0x18, 0x59, 0x9f, 0x4e, 0xd6, 0x61, | ||||
| 	0x49, 0xb9, 0xc9, 0x52, 0xfb, 0xe6, 0x1d, 0x93, 0x94, 0x66, 0x1b, 0xa2, 0x09, 0xdd, 0x94, 0x76, | ||||
| 	0x5e, 0xe8, 0x6a, 0x30, 0x31, 0x09, 0xdf, 0x26, 0xac, 0xf8, 0x91, 0xe1, 0x89, 0x90, 0xde, 0xd8, | ||||
| 	0x6a, 0x87, 0x48, 0xa1, 0xae, 0xc1, 0x6e, 0x20, 0xa7, 0xb1, 0xde, 0x71, 0x1b, 0x06, 0x97, 0xa9, | ||||
| 	0x5c, 0x85, 0x56, 0x2a, 0x4d, 0x65, 0x49, 0xbd, 0x8d, 0x11, 0xf6, 0xc3, 0x43, 0x60, 0x34, 0xd7, | ||||
| 	0x46, 0x4b, 0x64, 0x29, 0x5d, 0xdf, 0x84, 0x95, 0xc2, 0x2a, 0x63, 0x95, 0x1f, 0x73, 0x8b, 0xc2, | ||||
| 	0x19, 0xcd, 0x06, 0x61, 0x3c, 0xbd, 0x5f, 0x9a, 0xb0, 0xf9, 0x9e, 0x98, 0x4e, 0x75, 0x3a, 0x82, | ||||
| 	0x85, 0x09, 0xfc, 0x48, 0x4b, 0x9b, 0xf7, 0x1a, 0x47, 0x01, 0xd9, 0xa6, 0xf4, 0xdc, 0x24, 0x9c, | ||||
| 	0x26, 0x3e, 0x08, 0xef, 0x47, 0x1f, 0x45, 0x00, 0xa8, 0x34, 0x3a, 0xae, 0x34, 0xba, 0x19, 0x1d, | ||||
| 	0xc0, 0x96, 0xd2, 0x7f, 0xe4, 0x72, 0xbe, 0xc6, 0xe0, 0x4c, 0x9b, 0x2a, 0x81, 0xdd, 0x81, 0xb5, | ||||
| 	0x9a, 0xdb, 0x73, 0xf2, 0x4f, 0xea, 0xba, 0x10, 0x76, 0x5f, 0xa3, 0x23, 0xc9, 0x44, 0xea, 0x48, | ||||
| 	0x5b, 0x4f, 0xc9, 0x77, 0x25, 0xaa, 0xeb, 0xb0, 0x54, 0xe9, 0x46, 0x6c, 0x72, 0xa1, 0x34, 0x09, | ||||
| 	0x69, 0x8b, 0x90, 0x33, 0x0f, 0x63, 0x92, 0xd0, 0x20, 0x62, 0xed, 0x9a, 0x61, 0xf8, 0xce, 0xa3, | ||||
| 	0x0e, 0x88, 0x03, 0x2a, 0xef, 0x06, 0xec, 0x8f, 0xb0, 0x2f, 0x0a, 0x45, 0xd2, 0x14, 0x48, 0x11, | ||||
| 	0xb8, 0x5e, 0xd1, 0xb4, 0x9a, 0xec, 0xa5, 0x9a, 0xc3, 0x93, 0xfc, 0x9c, 0xc4, 0xd8, 0x0f, 0x2c, | ||||
| 	0xba, 0x81, 0xc9, 0x62, 0xb6, 0x44, 0xe5, 0xb4, 0x61, 0xb1, 0x74, 0xc8, 0x0b, 0x25, 0x1d, 0x5b, | ||||
| 	0xa6, 0x25, 0x45, 0x00, 0x43, 0xa1, 0xc3, 0x26, 0x4a, 0x9b, 0xb1, 0xcb, 0x35, 0xc8, 0xce, 0x94, | ||||
| 	0x60, 0x15, 0x93, 0x6c, 0x5f, 0x0c, 0x0b, 0x54, 0x05, 0xaf, 0x71, 0x46, 0xc0, 0xe8, 0xd4, 0x2a, | ||||
| 	0x56, 0x08, 0x8b, 0xda, 0x8b, 0xa0, 0xfd, 0xde, 0x2b, 0x9d, 0x3a, 0x52, 0xed, 0xa5, 0xa8, 0x07, | ||||
| 	0xdb, 0xef, 0x1f, 0x71, 0xa7, 0x52, 0x2d, 0x7c, 0x69, 0x91, 0x6d, 0x92, 0xcf, 0x0d, 0xd8, 0xaf, | ||||
| 	0x66, 0x4a, 0x7a, 0x97, 0x08, 0x95, 0x95, 0x16, 0x1d, 0xf7, 0x86, 0xe7, 0x2a, 0x0d, 0x40, 0x63, | ||||
| 	0x8c, 0xc6, 0x71, 0x0d, 0x76, 0x2b, 0xbf, 0x58, 0x39, 0x69, 0xb4, 0x46, 0xe9, 0x4f, 0x79, 0x6d, | ||||
| 	0x91, 0xd7, 0x55, 0xd8, 0x31, 0xa9, 0xe3, 0xb1, 0xf0, 0x82, 0x5b, 0x0c, 0x68, 0xa5, 0x0d, 0xf2, | ||||
| 	0x91, 0xd2, 0xb1, 0x19, 0xb1, 0x6d, 0x72, 0x3a, 0x1b, 0xff, 0x3b, 0x84, 0xff, 0x03, 0xd8, 0x4a, | ||||
| 	0x8c, 0x95, 0x38, 0xf9, 0xd8, 0xf0, 0xb2, 0x88, 0xc3, 0xd4, 0xe5, 0x00, 0xe5, 0x09, 0xdb, 0x0d, | ||||
| 	0xe3, 0xeb, 0x3d, 0x84, 0x7f, 0xcd, 0x00, 0x3b, 0xf9, 0x99, 0xbc, 0xc1, 0xfe, 0xf1, 0xf3, 0xc7, | ||||
| 	0xc7, 0x73, 0x8b, 0x7a, 0xed, 0xd0, 0x3e, 0x0d, 0x6b, 0x8a, 0x36, 0x00, 0x48, 0xda, 0x2b, 0xfd, | ||||
| 	0xab, 0x80, 0x7c, 0xee, 0xdf, 0xff, 0xeb, 0xfd, 0xdc, 0x80, 0xbb, 0x7f, 0x27, 0xd0, 0x47, 0x69, | ||||
| 	0xf1, 0x57, 0xd1, 0x72, 0xba, 0x98, 0xf3, 0xd3, 0x62, 0x22, 0x68, 0x9f, 0x62, 0xe1, 0xb3, 0x24, | ||||
| 	0xe9, 0xdd, 0x9a, 0xff, 0xa8, 0x3d, 0x31, 0x69, 0x8a, 0xf1, 0xb3, 0x24, 0x39, 0x2b, 0x7d, 0xef, | ||||
| 	0x53, 0xe8, 0xcc, 0x5c, 0x9f, 0xe2, 0xe8, 0x49, 0x58, 0xe6, 0xe7, 0x38, 0x0e, 0x24, 0x2b, 0xb5, | ||||
| 	0x7a, 0x5b, 0xd2, 0x88, 0x1b, 0xf5, 0x07, 0x60, 0xf6, 0xb6, 0x05, 0xb6, 0xb6, 0x7a, 0x47, 0xb0, | ||||
| 	0x77, 0xe6, 0xf5, 0x63, 0x29, 0xb1, 0xf0, 0x18, 0x9f, 0x11, 0xa6, 0xf7, 0xdb, 0xb9, 0xf9, 0x9c, | ||||
| 	0xc7, 0x15, 0x33, 0x1f, 0xeb, 0xc4, 0x04, 0x02, 0x16, 0x68, 0x9d, 0xd1, 0xa2, 0x92, 0xd1, 0x06, | ||||
| 	0x75, 0x1e, 0x01, 0xcc, 0x00, 0x5c, 0xe5, 0x25, 0xee, 0x10, 0xa8, 0x68, 0x68, 0xf1, 0x54, 0x8f, | ||||
| 	0x1d, 0x51, 0xbe, 0xf9, 0x21, 0xe5, 0x2b, 0x86, 0xaf, 0xc1, 0xa5, 0x44, 0x48, 0xec, 0x1b, 0x73, | ||||
| 	0x12, 0xea, 0x59, 0x24, 0xe4, 0x74, 0x60, 0x79, 0x6a, 0xa4, 0xb4, 0xd5, 0x1f, 0xe9, 0x3a, 0xec, | ||||
| 	0xcd, 0xc9, 0xbf, 0x36, 0x5e, 0x25, 0xe3, 0xf0, 0xe8, 0x4c, 0x24, 0xdd, 0x11, 0xff, 0x16, 0xa3, | ||||
| 	0xdb, 0x70, 0x75, 0xce, 0xed, 0x03, 0xbd, 0xe7, 0x13, 0xc1, 0x5f, 0xa9, 0x29, 0xa7, 0x1c, 0x2f, | ||||
| 	0x06, 0xa6, 0xfa, 0x91, 0x85, 0xd7, 0x32, 0x66, 0x6d, 0x8a, 0xc3, 0xa0, 0x3d, 0xa7, 0xdf, 0xce, | ||||
| 	0x07, 0x6a, 0xac, 0x52, 0xd1, 0xbb, 0xb0, 0x3e, 0xbd, 0x34, 0x79, 0x64, 0xc7, 0x4a, 0xa7, 0x2c, | ||||
| 	0xa2, 0x7b, 0x41, 0x2f, 0xeb, 0xd3, 0xf0, 0xf0, 0x05, 0xaa, 0x5a, 0xa4, 0xe0, 0x63, 0xb6, 0x16, | ||||
| 	0x5c, 0xee, 0x37, 0x1f, 0x35, 0x7e, 0x6a, 0xfc, 0xe3, 0xf7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb8, | ||||
| 	0x55, 0x41, 0x55, 0x01, 0x0c, 0x00, 0x00, | ||||
| 	// 2325 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x5b, 0x77, 0x1b, 0xb7, | ||||
| 	0xd5, 0xfd, 0x98, 0x58, 0x96, 0x08, 0x51, 0xb7, 0xd1, 0x0d, 0x92, 0x7c, 0x91, 0x98, 0xaf, 0x8e, | ||||
| 	0x12, 0x27, 0x5e, 0x8e, 0x9c, 0x95, 0x36, 0xa9, 0xd7, 0x4a, 0x65, 0xb9, 0x4e, 0xb8, 0xa2, 0x0b, | ||||
| 	0x33, 0x92, 0x9c, 0x5e, 0xd2, 0xa2, 0xe0, 0xcc, 0xe1, 0x10, 0xd5, 0x10, 0x18, 0x0f, 0x30, 0x62, | ||||
| 	0xf8, 0xd6, 0xc7, 0xbe, 0xf4, 0xa9, 0x7f, 0xa5, 0x6f, 0xfd, 0x11, 0xfd, 0x4b, 0x5d, 0x38, 0xc0, | ||||
| 	0x0c, 0x87, 0x8e, 0xec, 0x5e, 0x56, 0xdf, 0x34, 0xfb, 0x6c, 0x00, 0x07, 0x38, 0x1b, 0x07, 0x9b, | ||||
| 	0x22, 0x0f, 0xb4, 0x01, 0x3e, 0x1c, 0x82, 0xd6, 0x3c, 0x01, 0xcd, 0xa2, 0x54, 0x80, 0x34, 0x1a, | ||||
| 	0xf2, 0x6b, 0xc8, 0x59, 0xaa, 0x12, 0x21, 0x1f, 0x65, 0xb9, 0x32, 0x6a, 0x9b, 0x4e, 0xf3, 0x7a, | ||||
| 	0x5c, 0x83, 0x8b, 0xb4, 0xd7, 0xc9, 0xea, 0xd1, 0x89, 0x4e, 0x8e, 0x70, 0xe4, 0xd7, 0xc0, 0x73, | ||||
| 	0xf3, 0x0c, 0xb8, 0x69, 0x7f, 0x4f, 0x76, 0x27, 0xf0, 0x39, 0x4e, 0x78, 0x21, 0x86, 0xa0, 0x0d, | ||||
| 	0x1f, 0x66, 0x21, 0xbc, 0x2a, 0x40, 0x9b, 0xe0, 0x67, 0x84, 0xba, 0x05, 0x59, 0xee, 0x10, 0x66, | ||||
| 	0x4a, 0x0a, 0x6d, 0xec, 0x36, 0xf6, 0x6f, 0x85, 0x1b, 0x2e, 0xee, 0x07, 0x54, 0x13, 0xb4, 0xff, | ||||
| 	0xd2, 0x20, 0x7b, 0x6f, 0x99, 0x5e, 0x67, 0x4a, 0x6a, 0xf8, 0xef, 0xe7, 0x0f, 0x1e, 0x91, 0x55, | ||||
| 	0x7f, 0x08, 0xd5, 0x08, 0x36, 0xd4, 0xf4, 0x1d, 0x1c, 0xb4, 0xa2, 0xa7, 0xd7, 0x3b, 0xd1, 0xed, | ||||
| 	0x3f, 0x37, 0xc8, 0x72, 0x3d, 0x9f, 0x28, 0x07, 0x13, 0x50, 0x32, 0x7b, 0x0d, 0xb9, 0x16, 0x4a, | ||||
| 	0xe2, 0x6a, 0x0b, 0x61, 0xf9, 0x19, 0xac, 0x91, 0x19, 0x9e, 0x65, 0x22, 0xc6, 0x09, 0x17, 0x42, | ||||
| 	0xf7, 0x11, 0x6c, 0x93, 0xb9, 0x18, 0xae, 0x45, 0x04, 0x22, 0xa6, 0xef, 0x62, 0xa0, 0xfa, 0xb6, | ||||
| 	0x23, 0xa4, 0x92, 0x11, 0xd0, 0x5b, 0xbb, 0x8d, 0xfd, 0xdb, 0xa1, 0xfb, 0x08, 0x02, 0x72, 0x6b, | ||||
| 	0x30, 0xe4, 0x11, 0x9d, 0xd9, 0x6d, 0xec, 0xb7, 0x42, 0xfc, 0xbb, 0xfd, 0xd7, 0x55, 0xb2, 0x34, | ||||
| 	0x49, 0xe5, 0x58, 0x25, 0x4a, 0x06, 0x1f, 0x90, 0x65, 0x2c, 0x56, 0xa4, 0x52, 0x36, 0x9d, 0xd2, | ||||
| 	0x52, 0x89, 0xbf, 0xf4, 0xa9, 0x7d, 0x43, 0xda, 0x31, 0x64, 0x39, 0x44, 0xdc, 0x40, 0xcc, 0x54, | ||||
| 	0xaf, 0x5f, 0x68, 0x53, 0xb8, 0x8f, 0x2c, 0x17, 0xd7, 0xdc, 0x00, 0x13, 0x99, 0xcf, 0xfb, 0xfe, | ||||
| 	0x84, 0x79, 0x56, 0x23, 0x76, 0x1d, 0xaf, 0x93, 0x05, 0x9b, 0x64, 0x36, 0x82, 0x34, 0x65, 0xd5, | ||||
| 	0x86, 0x6e, 0xdb, 0xcf, 0x4e, 0x1c, 0x3c, 0x20, 0x4b, 0x29, 0xd7, 0x86, 0x69, 0xd0, 0x76, 0x55, | ||||
| 	0x4b, 0xb8, 0x85, 0x84, 0x05, 0x0b, 0x9f, 0x3b, 0xb4, 0x13, 0x07, 0x9f, 0x12, 0x5f, 0x21, 0x96, | ||||
| 	0xf1, 0xe8, 0x8a, 0x27, 0x50, 0xa5, 0x3f, 0x83, 0xf4, 0x35, 0x17, 0xed, 0xba, 0x60, 0xb9, 0x87, | ||||
| 	0xf7, 0xc9, 0x92, 0x1f, 0x95, 0x72, 0x99, 0x14, 0x3c, 0x01, 0x7a, 0x7b, 0xb7, 0xb1, 0xdf, 0x0c, | ||||
| 	0x17, 0x1d, 0x7c, 0xec, 0xd1, 0xe0, 0xff, 0x89, 0x47, 0x98, 0xd2, 0xcc, 0x8c, 0x33, 0xa0, 0xb3, | ||||
| 	0x38, 0x6d, 0xcb, 0xa1, 0x67, 0xfa, 0x62, 0x9c, 0x41, 0xf0, 0x25, 0xa1, 0x7a, 0xa0, 0x8a, 0x34, | ||||
| 	0x66, 0x39, 0x0c, 0x61, 0xd8, 0x83, 0x9c, 0x65, 0x5c, 0xeb, 0x91, 0xca, 0x63, 0x3a, 0xb7, 0xdb, | ||||
| 	0xd8, 0x9f, 0xfb, 0x62, 0xa6, 0xcf, 0x53, 0x0d, 0xe1, 0x86, 0xa3, 0x85, 0x9e, 0xd5, 0xf5, 0xa4, | ||||
| 	0x60, 0x8f, 0xb4, 0x46, 0x42, 0x4e, 0x72, 0x6f, 0x62, 0x32, 0xf3, 0x16, 0x2b, 0x53, 0x7e, 0x8f, | ||||
| 	0x2c, 0xd4, 0x8e, 0xfd, 0x93, 0xc7, 0x94, 0xb8, 0x44, 0x26, 0xe0, 0x27, 0x8f, 0x83, 0x67, 0x64, | ||||
| 	0x1d, 0x0b, 0xf2, 0xa3, 0x72, 0xcc, 0xef, 0x36, 0xf6, 0xe7, 0x0f, 0x16, 0x1f, 0xd9, 0xba, 0x77, | ||||
| 	0xba, 0x87, 0x71, 0x9c, 0x83, 0xd6, 0xe1, 0xea, 0x84, 0x3c, 0x29, 0xc9, 0x63, 0xb2, 0x56, 0x5b, | ||||
| 	0x28, 0x2b, 0x7a, 0xa9, 0x88, 0xec, 0x14, 0x6b, 0xb8, 0x5e, 0x30, 0x89, 0x75, 0x31, 0xd4, 0xc9, | ||||
| 	0x82, 0x1d, 0xd2, 0x7c, 0xa5, 0x34, 0x4b, 0xe1, 0x1a, 0x52, 0xba, 0xee, 0x74, 0xf9, 0x4a, 0xe9, | ||||
| 	0x63, 0xfb, 0x1d, 0xfc, 0xb4, 0xba, 0x62, 0xba, 0xc8, 0xb2, 0x54, 0x40, 0xcc, 0xb0, 0x53, 0xd8, | ||||
| 	0x8a, 0x6e, 0xa0, 0x54, 0xd7, 0x5d, 0xfc, 0xdc, 0x87, 0xcf, 0x6d, 0xb4, 0x13, 0x07, 0x0f, 0x49, | ||||
| 	0x73, 0xb2, 0xf8, 0xe6, 0x8d, 0xf9, 0xcf, 0x65, 0x65, 0x0a, 0x77, 0x09, 0x19, 0xf2, 0x68, 0x60, | ||||
| 	0xcf, 0x50, 0xc4, 0xf4, 0x1e, 0xaa, 0xbd, 0xe9, 0x11, 0x54, 0xd3, 0x42, 0xca, 0x0b, 0x19, 0x0d, | ||||
| 	0xec, 0x7d, 0xb5, 0x55, 0xbc, 0x6f, 0xb3, 0xfc, 0xa2, 0xf1, 0x38, 0x6c, 0x95, 0x38, 0x16, 0x72, | ||||
| 	0x9b, 0xcc, 0x16, 0x82, 0x0d, 0x55, 0x0c, 0x74, 0xb7, 0x64, 0xdc, 0x2e, 0xc4, 0x89, 0x8a, 0x21, | ||||
| 	0xb8, 0x47, 0x9a, 0xd1, 0x80, 0x1b, 0x17, 0xdd, 0x2b, 0xa3, 0x73, 0x16, 0xc3, 0xf8, 0x47, 0x24, | ||||
| 	0xc0, 0x8d, 0x1d, 0x30, 0x5e, 0x98, 0x01, 0x33, 0x22, 0xba, 0x02, 0x43, 0x3f, 0xc0, 0x54, 0x96, | ||||
| 	0x5d, 0xe4, 0xb0, 0x30, 0x83, 0x0b, 0xc4, 0x6d, 0x39, 0x61, 0xc8, 0x45, 0xca, 0xb8, 0xdb, 0x0b, | ||||
| 	0xfd, 0x10, 0x4b, 0xde, 0x42, 0xd0, 0xef, 0xcf, 0xb6, 0xa7, 0xdc, 0xb6, 0x97, 0x27, 0x07, 0x8c, | ||||
| 	0x47, 0x91, 0x2a, 0xa4, 0x61, 0x51, 0x0e, 0xdc, 0x58, 0x89, 0x3c, 0xdc, 0x6d, 0xec, 0xcf, 0x86, | ||||
| 	0x1b, 0x3e, 0x7e, 0xe8, 0xc2, 0x47, 0x3e, 0x6a, 0x05, 0x55, 0x8e, 0x90, 0x7c, 0x08, 0xf4, 0xc0, | ||||
| 	0x09, 0xca, 0x63, 0xa7, 0x7c, 0x68, 0xf7, 0x3a, 0x57, 0x89, 0xf4, 0x09, 0x86, 0xab, 0xef, 0xe0, | ||||
| 	0x43, 0xb2, 0x92, 0xf0, 0x21, 0xb0, 0xb2, 0xc5, 0xa9, 0x2b, 0x90, 0xf4, 0x53, 0x24, 0x2d, 0xd9, | ||||
| 	0x80, 0xef, 0xa7, 0x16, 0xb6, 0xd5, 0xc7, 0x77, 0x80, 0x5d, 0xc1, 0x98, 0x3e, 0x75, 0x13, 0x21, | ||||
| 	0xf0, 0x0d, 0x8c, 0x83, 0x17, 0xe4, 0xce, 0x88, 0x6b, 0x16, 0x29, 0x79, 0x0d, 0xb9, 0xd5, 0x53, | ||||
| 	0x4d, 0x5a, 0x43, 0x9d, 0xd0, 0x17, 0xf5, 0xdb, 0xb1, 0x35, 0xe2, 0xfa, 0xa8, 0x64, 0x3e, 0xaf, | ||||
| 	0x88, 0x27, 0x3a, 0x09, 0x7e, 0x41, 0xee, 0x72, 0xa9, 0x24, 0x2b, 0xb4, 0x4d, 0x87, 0xe7, 0x09, | ||||
| 	0x18, 0x36, 0xb5, 0xc1, 0x2e, 0x2e, 0xbc, 0x65, 0x49, 0x97, 0x1a, 0xf2, 0x0b, 0xa4, 0x1c, 0xd6, | ||||
| 	0xb6, 0xfb, 0x84, 0x6c, 0xe4, 0xa0, 0x55, 0x7a, 0x0d, 0xb1, 0x9b, 0xa5, 0x52, 0xe1, 0xb7, 0xa8, | ||||
| 	0xc2, 0xd5, 0x32, 0x6a, 0x87, 0x97, 0x1a, 0xfc, 0x98, 0x04, 0x90, 0x83, 0x2e, 0x52, 0xdb, 0x88, | ||||
| 	0xa4, 0xc9, 0xc7, 0x7d, 0x91, 0x02, 0x0d, 0x77, 0x1b, 0xfb, 0x33, 0xe1, 0x8a, 0x8f, 0x9c, 0x57, | ||||
| 	0x81, 0xe0, 0x27, 0x64, 0x51, 0x0f, 0x78, 0x9d, 0x7a, 0x8e, 0xe5, 0x5f, 0xd0, 0x03, 0x5e, 0xa3, | ||||
| 	0xed, 0x90, 0x26, 0x4a, 0x24, 0xb2, 0x4a, 0xba, 0x70, 0x27, 0x66, 0x81, 0x23, 0x2b, 0xa3, 0x2d, | ||||
| 	0x32, 0xa7, 0x4c, 0xe6, 0x54, 0x7a, 0x89, 0x0b, 0xcd, 0x2a, 0x93, 0xa1, 0x3a, 0x77, 0x48, 0xd3, | ||||
| 	0x86, 0xae, 0x79, 0x5a, 0x00, 0x7d, 0xe9, 0xee, 0x99, 0x32, 0xd9, 0x4b, 0xfb, 0x6d, 0xd7, 0xb6, | ||||
| 	0x41, 0x11, 0x83, 0x34, 0xa2, 0x2f, 0x20, 0xa7, 0xdf, 0xe1, 0xcc, 0x0b, 0xca, 0x64, 0x9d, 0x0a, | ||||
| 	0x0c, 0x0e, 0xc8, 0xba, 0x57, 0xa9, 0x13, 0x68, 0xf9, 0xf0, 0xd1, 0x5f, 0xd9, 0x4a, 0x84, 0xab, | ||||
| 	0x2e, 0xe8, 0x44, 0x5a, 0xbe, 0xc2, 0x0f, 0xc8, 0x92, 0x56, 0x72, 0xcc, 0x32, 0x2d, 0x4b, 0x59, | ||||
| 	0xff, 0xc6, 0xef, 0x4b, 0xc9, 0x71, 0x57, 0x4b, 0xaf, 0xe9, 0x8f, 0xc9, 0x6a, 0xc5, 0xb3, 0xca, | ||||
| 	0x11, 0x11, 0xde, 0xc6, 0xdf, 0x62, 0x1e, 0xcb, 0x9e, 0x7b, 0xee, 0x02, 0x9d, 0x38, 0xf8, 0x35, | ||||
| 	0x79, 0x1f, 0xd5, 0x0c, 0x4c, 0xc2, 0x08, 0x07, 0xa5, 0x42, 0x5e, 0x41, 0x5c, 0x15, 0x56, 0xf4, | ||||
| 	0x99, 0x04, 0x88, 0x21, 0xa6, 0xdf, 0xd7, 0x65, 0xb2, 0xe7, 0x46, 0x9d, 0xc2, 0xa8, 0xab, 0xe5, | ||||
| 	0x31, 0x0e, 0xf1, 0x85, 0xee, 0xf4, 0x4f, 0x91, 0x1f, 0xb4, 0xc9, 0x42, 0x95, 0x09, 0xca, 0xe3, | ||||
| 	0x77, 0x4e, 0xff, 0x3e, 0x07, 0x14, 0xc4, 0x43, 0x12, 0xd4, 0x35, 0xce, 0x33, 0x7b, 0x78, 0xf4, | ||||
| 	0xf7, 0x78, 0xe4, 0x35, 0x91, 0x1f, 0x66, 0x59, 0x27, 0x0e, 0x3a, 0x64, 0x0f, 0x4f, 0x26, 0x29, | ||||
| 	0x78, 0x1e, 0xb3, 0x58, 0xa1, 0x63, 0xf0, 0xad, 0x3e, 0x52, 0xc3, 0xac, 0x30, 0x90, 0x53, 0x86, | ||||
| 	0x47, 0x78, 0x6f, 0x42, 0x7c, 0xae, 0xac, 0x73, 0x70, 0xb4, 0x23, 0xcf, 0xb2, 0x57, 0xb3, 0x6c, | ||||
| 	0x55, 0x98, 0xda, 0x1f, 0x5c, 0x6a, 0x1e, 0xc3, 0xd4, 0x3e, 0x23, 0x9b, 0x75, 0x0a, 0xea, 0x35, | ||||
| 	0x1a, 0x28, 0x0d, 0x92, 0x72, 0x64, 0xaf, 0xd7, 0xd8, 0x97, 0x55, 0xd0, 0xbe, 0xe2, 0x78, 0x0e, | ||||
| 	0xf9, 0x98, 0xa9, 0x6b, 0xc8, 0x73, 0x11, 0x03, 0xed, 0xb9, 0x5b, 0xeb, 0xf1, 0x33, 0x0f, 0x07, | ||||
| 	0xbb, 0xa4, 0x25, 0xb4, 0xbf, 0x03, 0x3d, 0xf5, 0x03, 0x8d, 0x30, 0x77, 0x22, 0x34, 0x4a, 0xff, | ||||
| 	0x99, 0xfa, 0xc1, 0xf6, 0x33, 0xdf, 0xb8, 0x85, 0xd4, 0x86, 0x4b, 0x57, 0xcc, 0x18, 0x0d, 0xce, | ||||
| 	0xb2, 0x8b, 0x74, 0x7c, 0xc0, 0xbd, 0xd7, 0x66, 0xa4, 0x58, 0x9f, 0x47, 0x46, 0xe5, 0x4e, 0xd9, | ||||
| 	0xe0, 0xf4, 0x67, 0x46, 0xea, 0x05, 0xa2, 0x28, 0xef, 0x2f, 0xc9, 0x1d, 0xfb, 0x0e, 0xa8, 0xdc, | ||||
| 	0x68, 0x96, 0xdb, 0xda, 0xa7, 0x62, 0x28, 0xec, 0x61, 0x3a, 0x47, 0x46, 0xfb, 0x98, 0xc7, 0x56, | ||||
| 	0xc9, 0x09, 0xb9, 0x81, 0x63, 0xcb, 0xa8, 0x2c, 0xdb, 0x03, 0xb2, 0x34, 0x82, 0x9e, 0xb5, 0x9e, | ||||
| 	0x4a, 0x32, 0xe7, 0x78, 0x12, 0xb7, 0xd0, 0x08, 0x7a, 0x68, 0x66, 0x4e, 0xd1, 0xf9, 0xbc, 0x4f, | ||||
| 	0x96, 0xb2, 0x5c, 0xa8, 0x5c, 0x98, 0x31, 0xcb, 0x81, 0x6b, 0x25, 0xe9, 0x00, 0x6b, 0xbb, 0x58, | ||||
| 	0xc2, 0x21, 0xa2, 0xc1, 0x57, 0x64, 0xc3, 0x16, 0x28, 0x8e, 0x21, 0x66, 0xe5, 0x4b, 0x85, 0xf6, | ||||
| 	0x8c, 0x0a, 0x7c, 0x74, 0x56, 0x1e, 0xbd, 0xee, 0xdb, 0xc2, 0xb5, 0x72, 0xc0, 0x94, 0x9b, 0x7b, | ||||
| 	0x4a, 0xb6, 0x63, 0xa1, 0x79, 0x2f, 0x05, 0x96, 0xf1, 0xdc, 0x48, 0x2b, 0xaa, 0xc2, 0xa8, 0x24, | ||||
| 	0xe7, 0xd2, 0x68, 0xfa, 0x47, 0xdc, 0x18, 0xf5, 0x8c, 0xae, 0x23, 0x1c, 0x56, 0xf1, 0xf6, 0xdf, | ||||
| 	0x9a, 0x64, 0xf3, 0x35, 0x57, 0x56, 0xed, 0x79, 0x87, 0xcc, 0xfa, 0x66, 0x83, 0xa6, 0x6c, 0xe6, | ||||
| 	0x8b, 0xc6, 0x41, 0x58, 0x22, 0xb6, 0x35, 0xaa, 0xc2, 0x30, 0xd5, 0x67, 0x28, 0xe7, 0x81, 0x35, | ||||
| 	0xd8, 0x3d, 0xe0, 0xb8, 0x0b, 0x25, 0x63, 0xe7, 0x49, 0x67, 0xc2, 0x2d, 0x55, 0x98, 0xb3, 0xfe, | ||||
| 	0x57, 0x7c, 0x08, 0x5f, 0x97, 0x8c, 0x73, 0x47, 0x08, 0x3e, 0x27, 0x5b, 0x42, 0xbe, 0x69, 0xf4, | ||||
| 	0xbb, 0x38, 0x7a, 0x43, 0xc8, 0x1b, 0x87, 0xbe, 0xc9, 0x2c, 0xdc, 0x7a, 0xa3, 0x59, 0x78, 0x44, | ||||
| 	0x56, 0xcb, 0x37, 0xad, 0x66, 0xa0, 0xd1, 0xad, 0xcd, 0x86, 0x2b, 0x3e, 0x34, 0xf1, 0xeb, 0xf6, | ||||
| 	0xa1, 0x2c, 0xfb, 0x41, 0x3f, 0xe5, 0x89, 0x46, 0xa3, 0xb6, 0x10, 0x96, 0xcf, 0xdb, 0x0b, 0x8b, | ||||
| 	0xd5, 0x6d, 0xe4, 0xec, 0x94, 0x8d, 0xdc, 0x23, 0xee, 0x45, 0x65, 0xb1, 0x1a, 0x72, 0x21, 0xd1, | ||||
| 	0x8d, 0x35, 0xc3, 0x79, 0xc4, 0x9e, 0x23, 0x64, 0x17, 0x98, 0xea, 0x88, 0x68, 0xbe, 0x5a, 0x61, | ||||
| 	0xab, 0xde, 0x09, 0xed, 0xcd, 0x2a, 0x1f, 0x02, 0xf8, 0xc1, 0x80, 0xb4, 0x4d, 0x89, 0xb8, 0x56, | ||||
| 	0xe1, 0xf1, 0x5f, 0x7a, 0x38, 0x38, 0x22, 0xf7, 0x46, 0xd0, 0xe3, 0x99, 0x40, 0x1f, 0x60, 0x1b, | ||||
| 	0xaf, 0x3d, 0x00, 0xf7, 0xe6, 0x38, 0xbd, 0xce, 0x63, 0x12, 0x3b, 0x8e, 0x75, 0x58, 0x23, 0xd9, | ||||
| 	0xab, 0xec, 0xd4, 0xfb, 0x84, 0x6c, 0xf8, 0x0d, 0xb1, 0x4c, 0xc8, 0x84, 0x99, 0x41, 0x0e, 0x7a, | ||||
| 	0xa0, 0xd2, 0x98, 0xb6, 0x70, 0x7f, 0xab, 0x6e, 0x7f, 0x5d, 0x21, 0x93, 0x8b, 0x32, 0x64, 0x8f, | ||||
| 	0xb6, 0x56, 0x8c, 0x42, 0x03, 0xcb, 0x44, 0xa4, 0xe9, 0x02, 0x2a, 0x6f, 0x65, 0x12, 0xba, 0xd4, | ||||
| 	0xd0, 0x15, 0x91, 0xb6, 0xa6, 0xe9, 0x9a, 0x4b, 0x7b, 0x41, 0x8a, 0x3c, 0xa5, 0x8b, 0x98, 0x55, | ||||
| 	0xd3, 0x21, 0x97, 0x79, 0x3a, 0x6d, 0xc0, 0x96, 0xfe, 0x85, 0x01, 0xfb, 0x8c, 0x6c, 0xde, 0x68, | ||||
| 	0xf3, 0x44, 0x8c, 0xc6, 0xf1, 0x66, 0x97, 0x27, 0xb0, 0x6f, 0x88, 0x8c, 0x95, 0x5d, 0x0b, 0xfb, | ||||
| 	0xc6, 0xba, 0xbb, 0xce, 0x22, 0x3b, 0x72, 0x28, 0xf6, 0x8d, 0x87, 0x64, 0x25, 0xe3, 0x39, 0x48, | ||||
| 	0xc3, 0x53, 0xa6, 0xc1, 0x18, 0x21, 0x13, 0x8d, 0xfe, 0xb1, 0x15, 0x2e, 0x97, 0x81, 0x73, 0x8f, | ||||
| 	0xdb, 0x9b, 0xf8, 0x3a, 0x99, 0x69, 0x91, 0x48, 0x6e, 0x8a, 0x1c, 0xd0, 0x4b, 0xb6, 0x42, 0xfa, | ||||
| 	0xda, 0xa8, 0xf3, 0x32, 0x6e, 0x0b, 0xe8, 0xf4, 0x86, 0x2e, 0xa6, 0xcf, 0x45, 0x5a, 0xe4, 0xa0, | ||||
| 	0x99, 0x51, 0x6c, 0x28, 0x12, 0xdb, 0xb3, 0x28, 0xc5, 0xca, 0xef, 0x20, 0xeb, 0xb8, 0x4e, 0xba, | ||||
| 	0x50, 0x27, 0x8e, 0x62, 0xfb, 0x9c, 0x9b, 0x24, 0x16, 0x3a, 0x52, 0x52, 0x42, 0x64, 0xa6, 0xa6, | ||||
| 	0xd8, 0x72, 0x97, 0x12, 0x39, 0xcf, 0x27, 0x94, 0xc9, 0x04, 0x4f, 0xc9, 0x8e, 0x4a, 0x34, 0x8b, | ||||
| 	0xb9, 0xe1, 0x2c, 0x07, 0xdb, 0x0c, 0xf1, 0xa2, 0xb0, 0x91, 0x90, 0xb1, 0x1a, 0xd1, 0x6d, 0x1c, | ||||
| 	0xbf, 0xa9, 0x12, 0xfd, 0x9c, 0x1b, 0x1e, 0x22, 0xc1, 0xde, 0x97, 0xef, 0x30, 0xfc, 0x86, 0xe6, | ||||
| 	0xbd, 0xf3, 0x86, 0xe6, 0xfd, 0x39, 0xd9, 0xea, 0xab, 0x3c, 0x82, 0xb2, 0xff, 0x15, 0x59, 0x6c, | ||||
| 	0x25, 0x1b, 0x0d, 0x20, 0xba, 0xa2, 0x77, 0x50, 0x3e, 0x1b, 0x48, 0x70, 0xcd, 0xe9, 0x12, 0xc3, | ||||
| 	0x47, 0x36, 0xda, 0xfe, 0x96, 0x7c, 0x34, 0xe9, 0x5a, 0xde, 0x30, 0x7c, 0x07, 0xbd, 0xc3, 0x6e, | ||||
| 	0xe7, 0x66, 0x61, 0xef, 0x11, 0x82, 0x6e, 0xd2, 0x19, 0x1c, 0xd7, 0xcd, 0xde, 0xf9, 0xf8, 0x93, | ||||
| 	0xb0, 0x89, 0xa8, 0xb5, 0x39, 0xed, 0xbf, 0x37, 0xc8, 0xa7, 0xff, 0xc9, 0x9c, 0xff, 0x5e, 0x9b, | ||||
| 	0xfc, 0x9f, 0x5c, 0xcb, 0xe9, 0xec, 0xdf, 0xbd, 0x29, 0xfb, 0xa0, 0xfe, 0x3b, 0xff, 0x58, 0x25, | ||||
| 	0x67, 0xfd, 0x7e, 0xfb, 0xa0, 0xfe, 0x1f, 0x90, 0x63, 0x95, 0x24, 0x10, 0x9f, 0xf5, 0xfb, 0x6f, | ||||
| 	0xcd, 0xb7, 0xfd, 0x2d, 0x59, 0x9f, 0x8c, 0x39, 0x85, 0xd1, 0x71, 0x69, 0xa9, 0x77, 0x48, 0xb3, | ||||
| 	0x90, 0xe2, 0x55, 0x81, 0x15, 0x75, 0xbf, 0xd1, 0xe7, 0x1c, 0xd0, 0x89, 0xa7, 0xcd, 0xf8, 0x3b, | ||||
| 	0xd3, 0x66, 0xbc, 0xfd, 0x94, 0xdc, 0xbd, 0x71, 0xca, 0xc3, 0x28, 0x82, 0xcc, 0x40, 0xfc, 0xd6, | ||||
| 	0xa9, 0xdb, 0xff, 0xb8, 0x55, 0xcf, 0xa8, 0x74, 0x5c, 0xb2, 0xaf, 0x6c, 0x93, 0xcd, 0x20, 0xd7, | ||||
| 	0x4a, 0x72, 0xe7, 0x68, 0x1a, 0xae, 0xc9, 0x7a, 0x0c, 0x1d, 0xcd, 0x5d, 0x42, 0x26, 0xd7, 0xdc, | ||||
| 	0x27, 0xd6, 0xac, 0x6e, 0x38, 0xfe, 0x8a, 0xc7, 0xdb, 0x82, 0xb5, 0x89, 0x2b, 0x4b, 0xa5, 0xf1, | ||||
| 	0x5d, 0x98, 0x09, 0xd7, 0x30, 0x8a, 0x35, 0x89, 0x4b, 0x23, 0xa5, 0x7f, 0xfc, 0x34, 0xcc, 0xde, | ||||
| 	0xf0, 0x34, 0xdc, 0x27, 0xf3, 0x7d, 0x1e, 0x41, 0x4f, 0xa9, 0x2b, 0xbb, 0xab, 0x39, 0xbc, 0x02, | ||||
| 	0xa4, 0x84, 0x3a, 0xb1, 0x9d, 0xa5, 0x22, 0x60, 0xfa, 0xee, 0xc7, 0x77, 0xab, 0x04, 0x31, 0xff, | ||||
| 	0x67, 0xe4, 0x6e, 0xcd, 0xff, 0x49, 0x65, 0x44, 0x7f, 0x6c, 0x6d, 0xab, 0xf7, 0x61, 0x1a, 0xbb, | ||||
| 	0xe7, 0x5c, 0xb8, 0x33, 0x21, 0x9d, 0x22, 0xe7, 0x74, 0x42, 0x09, 0x8e, 0xc9, 0x7b, 0xb5, 0x39, | ||||
| 	0x7e, 0x64, 0xf0, 0x98, 0x77, 0x78, 0x4b, 0xb8, 0xfc, 0xfd, 0x09, 0xf5, 0x64, 0xda, 0xeb, 0x1d, | ||||
| 	0x39, 0xaf, 0xf7, 0x21, 0x59, 0x11, 0x9a, 0x65, 0x03, 0xe5, 0xfe, 0x6d, 0x60, 0xdd, 0x7d, 0x4c, | ||||
| 	0x97, 0x31, 0x8b, 0x25, 0xa1, 0xbb, 0x16, 0x7f, 0xe9, 0xe1, 0x60, 0x9f, 0x2c, 0xd7, 0xcc, 0x99, | ||||
| 	0x36, 0xb6, 0x01, 0xad, 0xe0, 0x59, 0x2d, 0x56, 0xee, 0xec, 0xdc, 0xa2, 0xf6, 0x3d, 0xaf, 0x66, | ||||
| 	0xf5, 0x3f, 0x25, 0xc6, 0x42, 0x26, 0x34, 0xc0, 0x89, 0x03, 0x3f, 0x71, 0x67, 0x12, 0x41, 0xf3, | ||||
| 	0x50, 0x8e, 0xb0, 0x6e, 0xdd, 0xf6, 0xda, 0x1c, 0x30, 0xa3, 0x31, 0x5d, 0x75, 0xbd, 0xc3, 0x0f, | ||||
| 	0x3b, 0x75, 0xe1, 0xd0, 0x47, 0xdb, 0x9f, 0x91, 0xed, 0x89, 0xa0, 0x8e, 0x06, 0x3c, 0x4d, 0x41, | ||||
| 	0x26, 0x50, 0xfe, 0xea, 0xa0, 0x64, 0xb6, 0x7c, 0x41, 0x1a, 0xf8, 0x82, 0x94, 0x9f, 0xed, 0x9f, | ||||
| 	0x93, 0x9d, 0x1b, 0xc7, 0xf9, 0x36, 0x70, 0x07, 0x7f, 0xa8, 0x3b, 0xd0, 0x0f, 0x9d, 0x00, 0xcf, | ||||
| 	0x66, 0xbe, 0x6e, 0xfc, 0xa9, 0xf1, 0x7f, 0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xa0, 0x4a, 0xca, | ||||
| 	0x5a, 0xd7, 0x14, 0x00, 0x00, | ||||
| } | ||||
|   | ||||
							
								
								
									
										64
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_site_license.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/client_site_license.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -21,9 +21,9 @@ var _ = math.Inf | ||||
| const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||
|  | ||||
| type CMsgClientSiteInfo struct { | ||||
| 	SiteId                 *uint64  `protobuf:"varint,1,opt,name=site_id" json:"site_id,omitempty"` | ||||
| 	SiteName               *string  `protobuf:"bytes,2,opt,name=site_name" json:"site_name,omitempty"` | ||||
| 	AllowCachedCredentials *bool    `protobuf:"varint,3,opt,name=allow_cached_credentials" json:"allow_cached_credentials,omitempty"` | ||||
| 	SiteId                 *uint64  `protobuf:"varint,1,opt,name=site_id,json=siteId" json:"site_id,omitempty"` | ||||
| 	SiteName               *string  `protobuf:"bytes,2,opt,name=site_name,json=siteName" json:"site_name,omitempty"` | ||||
| 	AllowCachedCredentials *bool    `protobuf:"varint,3,opt,name=allow_cached_credentials,json=allowCachedCredentials" json:"allow_cached_credentials,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral   struct{} `json:"-"` | ||||
| 	XXX_unrecognized       []byte   `json:"-"` | ||||
| 	XXX_sizecache          int32    `json:"-"` | ||||
| @@ -283,11 +283,11 @@ func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_DiscardUnknown() { | ||||
| var xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo proto.InternalMessageInfo | ||||
|  | ||||
| type CMsgClientSiteLicenseGetContentCacheInfoResponse struct { | ||||
| 	UseCache             *bool    `protobuf:"varint,1,opt,name=use_cache" json:"use_cache,omitempty"` | ||||
| 	Ipv4Address          *uint32  `protobuf:"varint,2,opt,name=ipv4_address" json:"ipv4_address,omitempty"` | ||||
| 	PortNumber           *uint32  `protobuf:"varint,3,opt,name=port_number" json:"port_number,omitempty"` | ||||
| 	P2PGroup             *uint32  `protobuf:"varint,4,opt,name=p2p_group" json:"p2p_group,omitempty"` | ||||
| 	IpAddress            *string  `protobuf:"bytes,5,opt,name=ip_address" json:"ip_address,omitempty"` | ||||
| 	UseCache             *bool    `protobuf:"varint,1,opt,name=use_cache,json=useCache" json:"use_cache,omitempty"` | ||||
| 	Ipv4Address          *uint32  `protobuf:"varint,2,opt,name=ipv4_address,json=ipv4Address" json:"ipv4_address,omitempty"` | ||||
| 	PortNumber           *uint32  `protobuf:"varint,3,opt,name=port_number,json=portNumber" json:"port_number,omitempty"` | ||||
| 	P2PGroup             *uint32  `protobuf:"varint,4,opt,name=p2p_group,json=p2pGroup" json:"p2p_group,omitempty"` | ||||
| 	IpAddress            *string  `protobuf:"bytes,5,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -372,26 +372,30 @@ func init() { | ||||
| } | ||||
|  | ||||
| var fileDescriptor_0a32817a56a37a6e = []byte{ | ||||
| 	// 335 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x41, 0x4b, 0xfb, 0x40, | ||||
| 	0x14, 0xc4, 0xff, 0xfb, 0xb7, 0xa1, 0xed, 0xd3, 0x20, 0x5d, 0x3d, 0x04, 0x41, 0x08, 0x81, 0x4a, | ||||
| 	0xf0, 0x50, 0xa4, 0x08, 0x82, 0x9e, 0x34, 0x07, 0x15, 0xf4, 0x62, 0x6f, 0x5e, 0xc2, 0x36, 0x79, | ||||
| 	0xb6, 0x8b, 0x9b, 0xdd, 0x25, 0x6f, 0x53, 0xaf, 0x7e, 0x0b, 0xbf, 0xae, 0x64, 0x5b, 0x0a, 0x15, | ||||
| 	0x2d, 0xbd, 0xee, 0xce, 0x6f, 0x66, 0x98, 0x07, 0x43, 0x72, 0x28, 0xaa, 0x0a, 0x89, 0xc4, 0x0c, | ||||
| 	0x29, 0x27, 0xe9, 0x50, 0xc9, 0x02, 0x35, 0x61, 0xa1, 0x24, 0x6a, 0x37, 0xb2, 0xb5, 0x71, 0xe6, | ||||
| 	0x24, 0xda, 0x94, 0x4d, 0x05, 0xe1, 0xf2, 0x27, 0x79, 0x05, 0x9e, 0x3d, 0xd3, 0x2c, 0xf3, 0xea, | ||||
| 	0x89, 0x74, 0xf8, 0xa8, 0xdf, 0x0c, 0x3f, 0x84, 0x6e, 0x6b, 0x95, 0xcb, 0x32, 0x62, 0x31, 0x4b, | ||||
| 	0x3b, 0x7c, 0x00, 0x7d, 0xff, 0xa0, 0x45, 0x85, 0xd1, 0xff, 0x98, 0xa5, 0x7d, 0x1e, 0x43, 0x24, | ||||
| 	0x94, 0x32, 0x1f, 0x79, 0x21, 0x8a, 0x39, 0x96, 0x79, 0x51, 0x63, 0x89, 0xda, 0x49, 0xa1, 0x28, | ||||
| 	0xda, 0x8b, 0x59, 0xda, 0x4b, 0x46, 0x70, 0xba, 0xe9, 0xfd, 0xb4, 0xac, 0x96, 0xcd, 0xb1, 0x78, | ||||
| 	0x37, 0x8d, 0xe3, 0x21, 0x04, 0xc2, 0xda, 0x55, 0x48, 0x98, 0xdc, 0xc0, 0x70, 0xab, 0xfe, 0x05, | ||||
| 	0xc9, 0x1a, 0x4d, 0xc8, 0x39, 0x74, 0xb1, 0x46, 0x6a, 0x94, 0xf3, 0x64, 0x70, 0xcd, 0xc6, 0xc9, | ||||
| 	0x15, 0x9c, 0xfd, 0x0a, 0xdf, 0xa3, 0xbb, 0x5d, 0x08, 0xa9, 0xc4, 0x54, 0xe1, 0x04, 0x85, 0xa3, | ||||
| 	0x9f, 0xa9, 0x13, 0x18, 0xed, 0x06, 0x6e, 0x8b, 0x6f, 0x4d, 0xa9, 0x15, 0xf9, 0x71, 0xc2, 0xe4, | ||||
| 	0x1c, 0xd2, 0xbf, 0x4c, 0x33, 0xa3, 0x1d, 0x6a, 0x97, 0xb5, 0xab, 0xb5, 0x63, 0x27, 0x5f, 0x0c, | ||||
| 	0x2e, 0x76, 0x15, 0xaf, 0x3b, 0x0c, 0xa0, 0xdf, 0x10, 0x2e, 0xb7, 0xf7, 0x2d, 0x7a, 0xfc, 0x18, | ||||
| 	0x0e, 0xa4, 0x5d, 0x5c, 0xe6, 0xa2, 0x2c, 0x6b, 0xa4, 0x55, 0x13, 0x7e, 0x04, 0xfb, 0xd6, 0xd4, | ||||
| 	0x2e, 0xd7, 0x4d, 0x35, 0xc5, 0xda, 0x5f, 0x26, 0x6c, 0x69, 0x3b, 0xb6, 0xf9, 0xac, 0x36, 0x8d, | ||||
| 	0x8d, 0x3a, 0xfe, 0x89, 0x03, 0x48, 0xbb, 0x66, 0x83, 0xf6, 0xc4, 0x77, 0xc1, 0x03, 0xfb, 0x64, | ||||
| 	0xff, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0x09, 0x2f, 0x9f, 0xe9, 0x65, 0x02, 0x00, 0x00, | ||||
| 	// 393 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x4f, 0x8b, 0xd3, 0x40, | ||||
| 	0x14, 0x77, 0x74, 0xe3, 0x26, 0x6f, 0xed, 0x65, 0x10, 0x0d, 0x2e, 0x8b, 0x35, 0xb0, 0x12, 0x3c, | ||||
| 	0x2c, 0x52, 0x14, 0xc4, 0x83, 0xb0, 0x46, 0x58, 0x17, 0xb4, 0x87, 0xe9, 0x07, 0x08, 0xd3, 0xe4, | ||||
| 	0xd9, 0x0e, 0x4e, 0x66, 0x86, 0xbc, 0x49, 0xbd, 0x7a, 0xf0, 0xfb, 0xf9, 0x95, 0x64, 0x26, 0xad, | ||||
| 	0x5a, 0xb0, 0xd2, 0xe3, 0xef, 0xcf, 0xfc, 0xde, 0x2f, 0xef, 0x05, 0x2e, 0xc9, 0xa3, 0xec, 0x3a, | ||||
| 	0x24, 0x92, 0x2b, 0xa4, 0x9a, 0x94, 0x47, 0xad, 0x1a, 0x34, 0x84, 0x8d, 0x56, 0x68, 0xfc, 0x95, | ||||
| 	0xeb, 0xad, 0xb7, 0x4f, 0xf2, 0x7d, 0xdb, 0x52, 0x12, 0x8e, 0x4a, 0xf1, 0x83, 0x01, 0xaf, 0x3e, | ||||
| 	0xd3, 0xaa, 0x8a, 0xf6, 0x85, 0xf2, 0x78, 0x6b, 0xbe, 0x58, 0xfe, 0x18, 0x4e, 0x43, 0x56, 0xad, | ||||
| 	0xda, 0x9c, 0x4d, 0x59, 0x79, 0x22, 0xee, 0x07, 0x78, 0xdb, 0xf2, 0x73, 0xc8, 0xa2, 0x60, 0x64, | ||||
| 	0x87, 0xf9, 0xdd, 0x29, 0x2b, 0x33, 0x91, 0x06, 0x62, 0x2e, 0x3b, 0xe4, 0x6f, 0x20, 0x97, 0x5a, | ||||
| 	0xdb, 0x6f, 0x75, 0x23, 0x9b, 0x35, 0xb6, 0x75, 0xd3, 0x63, 0x8b, 0xc6, 0x2b, 0xa9, 0x29, 0xbf, | ||||
| 	0x37, 0x65, 0x65, 0x2a, 0x1e, 0x45, 0xbd, 0x8a, 0x72, 0xf5, 0x47, 0x2d, 0x5e, 0xc3, 0xc5, 0x7e, | ||||
| 	0x8b, 0x4f, 0xe3, 0x57, 0x54, 0x6b, 0x6c, 0xbe, 0xda, 0xc1, 0xf3, 0x87, 0x90, 0x48, 0xe7, 0xb6, | ||||
| 	0x75, 0x26, 0x62, 0x04, 0xc5, 0x07, 0xb8, 0xfc, 0xef, 0x33, 0x81, 0xe4, 0xac, 0x21, 0xe4, 0xe7, | ||||
| 	0x70, 0x8a, 0x3d, 0xd2, 0xa0, 0x7d, 0x0c, 0x48, 0xde, 0xb2, 0x99, 0xd8, 0x31, 0xc5, 0x3b, 0x78, | ||||
| 	0xfe, 0xcf, 0x94, 0x1b, 0xf4, 0xd7, 0x1b, 0xa9, 0xb4, 0x5c, 0x6a, 0x5c, 0xa0, 0xf4, 0x74, 0xa0, | ||||
| 	0x45, 0x03, 0x57, 0xc7, 0xbd, 0x3f, 0xaa, 0x4e, 0x18, 0x42, 0xc1, 0x1d, 0xd7, 0x3b, 0x11, 0x23, | ||||
| 	0x28, 0x5e, 0x40, 0x79, 0x68, 0x48, 0x65, 0x8d, 0x47, 0xe3, 0xe3, 0x56, 0xc3, 0xf5, 0x8a, 0x9f, | ||||
| 	0x0c, 0x5e, 0x1e, 0x6b, 0xfe, 0xab, 0x53, 0x36, 0x10, 0x8e, 0xa7, 0x8b, 0xad, 0x52, 0x91, 0x0e, | ||||
| 	0x84, 0xd1, 0xc8, 0x9f, 0xc1, 0x03, 0xe5, 0x36, 0xaf, 0x6a, 0xd9, 0xb6, 0x3d, 0xd2, 0xae, 0xda, | ||||
| 	0x59, 0xe0, 0xae, 0x47, 0x8a, 0x3f, 0x85, 0x33, 0x67, 0x7b, 0x5f, 0x9b, 0xa1, 0x5b, 0x62, 0x1f, | ||||
| 	0xef, 0x3d, 0x11, 0x10, 0xa8, 0x79, 0x64, 0xc2, 0x00, 0x37, 0x73, 0xf5, 0xaa, 0xb7, 0x83, 0xcb, | ||||
| 	0x4f, 0xa2, 0x9c, 0xba, 0x99, 0xbb, 0x09, 0x98, 0x5f, 0x00, 0x28, 0xf7, 0x3b, 0x3e, 0x89, 0x3f, | ||||
| 	0x56, 0xa6, 0xdc, 0x36, 0xfc, 0x7d, 0xf2, 0x91, 0x7d, 0x67, 0x77, 0x7e, 0x05, 0x00, 0x00, 0xff, | ||||
| 	0xff, 0x35, 0x87, 0xfb, 0x5f, 0xef, 0x02, 0x00, 0x00, | ||||
| } | ||||
|   | ||||
							
								
								
									
										180
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/content_manifest.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										180
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/protobuf/content_manifest.pb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -20,6 +20,46 @@ var _ = math.Inf | ||||
| // proto package protobuf to be updated. | ||||
| const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package | ||||
|  | ||||
| type EContentDeltaChunkDataLocation int32 | ||||
|  | ||||
| const ( | ||||
| 	EContentDeltaChunkDataLocation_k_EContentDeltaChunkDataLocationInProtobuf    EContentDeltaChunkDataLocation = 0 | ||||
| 	EContentDeltaChunkDataLocation_k_EContentDeltaChunkDataLocationAfterProtobuf EContentDeltaChunkDataLocation = 1 | ||||
| ) | ||||
|  | ||||
| var EContentDeltaChunkDataLocation_name = map[int32]string{ | ||||
| 	0: "k_EContentDeltaChunkDataLocationInProtobuf", | ||||
| 	1: "k_EContentDeltaChunkDataLocationAfterProtobuf", | ||||
| } | ||||
|  | ||||
| var EContentDeltaChunkDataLocation_value = map[string]int32{ | ||||
| 	"k_EContentDeltaChunkDataLocationInProtobuf":    0, | ||||
| 	"k_EContentDeltaChunkDataLocationAfterProtobuf": 1, | ||||
| } | ||||
|  | ||||
| func (x EContentDeltaChunkDataLocation) Enum() *EContentDeltaChunkDataLocation { | ||||
| 	p := new(EContentDeltaChunkDataLocation) | ||||
| 	*p = x | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| func (x EContentDeltaChunkDataLocation) String() string { | ||||
| 	return proto.EnumName(EContentDeltaChunkDataLocation_name, int32(x)) | ||||
| } | ||||
|  | ||||
| func (x *EContentDeltaChunkDataLocation) UnmarshalJSON(data []byte) error { | ||||
| 	value, err := proto.UnmarshalJSONEnum(EContentDeltaChunkDataLocation_value, data, "EContentDeltaChunkDataLocation") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*x = EContentDeltaChunkDataLocation(value) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (EContentDeltaChunkDataLocation) EnumDescriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_e3cda137a29253ba, []int{0} | ||||
| } | ||||
|  | ||||
| type ContentManifestPayload struct { | ||||
| 	Mappings             []*ContentManifestPayload_FileMapping `protobuf:"bytes,1,rep,name=mappings" json:"mappings,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}                              `json:"-"` | ||||
| @@ -63,8 +103,8 @@ type ContentManifestPayload_FileMapping struct { | ||||
| 	Filename             *string                                         `protobuf:"bytes,1,opt,name=filename" json:"filename,omitempty"` | ||||
| 	Size                 *uint64                                         `protobuf:"varint,2,opt,name=size" json:"size,omitempty"` | ||||
| 	Flags                *uint32                                         `protobuf:"varint,3,opt,name=flags" json:"flags,omitempty"` | ||||
| 	ShaFilename          []byte                                          `protobuf:"bytes,4,opt,name=sha_filename" json:"sha_filename,omitempty"` | ||||
| 	ShaContent           []byte                                          `protobuf:"bytes,5,opt,name=sha_content" json:"sha_content,omitempty"` | ||||
| 	ShaFilename          []byte                                          `protobuf:"bytes,4,opt,name=sha_filename,json=shaFilename" json:"sha_filename,omitempty"` | ||||
| 	ShaContent           []byte                                          `protobuf:"bytes,5,opt,name=sha_content,json=shaContent" json:"sha_content,omitempty"` | ||||
| 	Chunks               []*ContentManifestPayload_FileMapping_ChunkData `protobuf:"bytes,6,rep,name=chunks" json:"chunks,omitempty"` | ||||
| 	Linktarget           *string                                         `protobuf:"bytes,7,opt,name=linktarget" json:"linktarget,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}                                        `json:"-"` | ||||
| @@ -150,8 +190,8 @@ type ContentManifestPayload_FileMapping_ChunkData struct { | ||||
| 	Sha                  []byte   `protobuf:"bytes,1,opt,name=sha" json:"sha,omitempty"` | ||||
| 	Crc                  *uint32  `protobuf:"fixed32,2,opt,name=crc" json:"crc,omitempty"` | ||||
| 	Offset               *uint64  `protobuf:"varint,3,opt,name=offset" json:"offset,omitempty"` | ||||
| 	CbOriginal           *uint32  `protobuf:"varint,4,opt,name=cb_original" json:"cb_original,omitempty"` | ||||
| 	CbCompressed         *uint32  `protobuf:"varint,5,opt,name=cb_compressed" json:"cb_compressed,omitempty"` | ||||
| 	CbOriginal           *uint32  `protobuf:"varint,4,opt,name=cb_original,json=cbOriginal" json:"cb_original,omitempty"` | ||||
| 	CbCompressed         *uint32  `protobuf:"varint,5,opt,name=cb_compressed,json=cbCompressed" json:"cb_compressed,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -222,15 +262,15 @@ func (m *ContentManifestPayload_FileMapping_ChunkData) GetCbCompressed() uint32 | ||||
| } | ||||
|  | ||||
| type ContentManifestMetadata struct { | ||||
| 	DepotId              *uint32  `protobuf:"varint,1,opt,name=depot_id" json:"depot_id,omitempty"` | ||||
| 	GidManifest          *uint64  `protobuf:"varint,2,opt,name=gid_manifest" json:"gid_manifest,omitempty"` | ||||
| 	CreationTime         *uint32  `protobuf:"varint,3,opt,name=creation_time" json:"creation_time,omitempty"` | ||||
| 	FilenamesEncrypted   *bool    `protobuf:"varint,4,opt,name=filenames_encrypted" json:"filenames_encrypted,omitempty"` | ||||
| 	CbDiskOriginal       *uint64  `protobuf:"varint,5,opt,name=cb_disk_original" json:"cb_disk_original,omitempty"` | ||||
| 	CbDiskCompressed     *uint64  `protobuf:"varint,6,opt,name=cb_disk_compressed" json:"cb_disk_compressed,omitempty"` | ||||
| 	UniqueChunks         *uint32  `protobuf:"varint,7,opt,name=unique_chunks" json:"unique_chunks,omitempty"` | ||||
| 	CrcEncrypted         *uint32  `protobuf:"varint,8,opt,name=crc_encrypted" json:"crc_encrypted,omitempty"` | ||||
| 	CrcClear             *uint32  `protobuf:"varint,9,opt,name=crc_clear" json:"crc_clear,omitempty"` | ||||
| 	DepotId              *uint32  `protobuf:"varint,1,opt,name=depot_id,json=depotId" json:"depot_id,omitempty"` | ||||
| 	GidManifest          *uint64  `protobuf:"varint,2,opt,name=gid_manifest,json=gidManifest" json:"gid_manifest,omitempty"` | ||||
| 	CreationTime         *uint32  `protobuf:"varint,3,opt,name=creation_time,json=creationTime" json:"creation_time,omitempty"` | ||||
| 	FilenamesEncrypted   *bool    `protobuf:"varint,4,opt,name=filenames_encrypted,json=filenamesEncrypted" json:"filenames_encrypted,omitempty"` | ||||
| 	CbDiskOriginal       *uint64  `protobuf:"varint,5,opt,name=cb_disk_original,json=cbDiskOriginal" json:"cb_disk_original,omitempty"` | ||||
| 	CbDiskCompressed     *uint64  `protobuf:"varint,6,opt,name=cb_disk_compressed,json=cbDiskCompressed" json:"cb_disk_compressed,omitempty"` | ||||
| 	UniqueChunks         *uint32  `protobuf:"varint,7,opt,name=unique_chunks,json=uniqueChunks" json:"unique_chunks,omitempty"` | ||||
| 	CrcEncrypted         *uint32  `protobuf:"varint,8,opt,name=crc_encrypted,json=crcEncrypted" json:"crc_encrypted,omitempty"` | ||||
| 	CrcClear             *uint32  `protobuf:"varint,9,opt,name=crc_clear,json=crcClear" json:"crc_clear,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -364,10 +404,11 @@ func (m *ContentManifestSignature) GetSignature() []byte { | ||||
| } | ||||
|  | ||||
| type ContentDeltaChunks struct { | ||||
| 	DepotId              *uint32                          `protobuf:"varint,1,opt,name=depot_id" json:"depot_id,omitempty"` | ||||
| 	ManifestIdSource     *uint64                          `protobuf:"varint,2,opt,name=manifest_id_source" json:"manifest_id_source,omitempty"` | ||||
| 	ManifestIdTarget     *uint64                          `protobuf:"varint,3,opt,name=manifest_id_target" json:"manifest_id_target,omitempty"` | ||||
| 	DepotId              *uint32                          `protobuf:"varint,1,opt,name=depot_id,json=depotId" json:"depot_id,omitempty"` | ||||
| 	ManifestIdSource     *uint64                          `protobuf:"varint,2,opt,name=manifest_id_source,json=manifestIdSource" json:"manifest_id_source,omitempty"` | ||||
| 	ManifestIdTarget     *uint64                          `protobuf:"varint,3,opt,name=manifest_id_target,json=manifestIdTarget" json:"manifest_id_target,omitempty"` | ||||
| 	DeltaChunks          []*ContentDeltaChunks_DeltaChunk `protobuf:"bytes,4,rep,name=deltaChunks" json:"deltaChunks,omitempty"` | ||||
| 	ChunkDataLocation    *EContentDeltaChunkDataLocation  `protobuf:"varint,5,opt,name=chunk_data_location,json=chunkDataLocation,enum=EContentDeltaChunkDataLocation,def=0" json:"chunk_data_location,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}                         `json:"-"` | ||||
| 	XXX_unrecognized     []byte                           `json:"-"` | ||||
| 	XXX_sizecache        int32                            `json:"-"` | ||||
| @@ -398,6 +439,8 @@ func (m *ContentDeltaChunks) XXX_DiscardUnknown() { | ||||
|  | ||||
| var xxx_messageInfo_ContentDeltaChunks proto.InternalMessageInfo | ||||
|  | ||||
| const Default_ContentDeltaChunks_ChunkDataLocation EContentDeltaChunkDataLocation = EContentDeltaChunkDataLocation_k_EContentDeltaChunkDataLocationInProtobuf | ||||
|  | ||||
| func (m *ContentDeltaChunks) GetDepotId() uint32 { | ||||
| 	if m != nil && m.DepotId != nil { | ||||
| 		return *m.DepotId | ||||
| @@ -426,12 +469,20 @@ func (m *ContentDeltaChunks) GetDeltaChunks() []*ContentDeltaChunks_DeltaChunk { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *ContentDeltaChunks) GetChunkDataLocation() EContentDeltaChunkDataLocation { | ||||
| 	if m != nil && m.ChunkDataLocation != nil { | ||||
| 		return *m.ChunkDataLocation | ||||
| 	} | ||||
| 	return Default_ContentDeltaChunks_ChunkDataLocation | ||||
| } | ||||
|  | ||||
| type ContentDeltaChunks_DeltaChunk struct { | ||||
| 	ShaSource            []byte   `protobuf:"bytes,1,opt,name=sha_source" json:"sha_source,omitempty"` | ||||
| 	ShaTarget            []byte   `protobuf:"bytes,2,opt,name=sha_target" json:"sha_target,omitempty"` | ||||
| 	SizeOriginal         *uint32  `protobuf:"varint,3,opt,name=size_original" json:"size_original,omitempty"` | ||||
| 	PatchMethod          *uint32  `protobuf:"varint,4,opt,name=patch_method" json:"patch_method,omitempty"` | ||||
| 	ShaSource            []byte   `protobuf:"bytes,1,opt,name=sha_source,json=shaSource" json:"sha_source,omitempty"` | ||||
| 	ShaTarget            []byte   `protobuf:"bytes,2,opt,name=sha_target,json=shaTarget" json:"sha_target,omitempty"` | ||||
| 	SizeOriginal         *uint32  `protobuf:"varint,3,opt,name=size_original,json=sizeOriginal" json:"size_original,omitempty"` | ||||
| 	PatchMethod          *uint32  `protobuf:"varint,4,opt,name=patch_method,json=patchMethod" json:"patch_method,omitempty"` | ||||
| 	Chunk                []byte   `protobuf:"bytes,5,opt,name=chunk" json:"chunk,omitempty"` | ||||
| 	SizeDelta            *uint32  `protobuf:"varint,6,opt,name=size_delta,json=sizeDelta" json:"size_delta,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| @@ -497,7 +548,15 @@ func (m *ContentDeltaChunks_DeltaChunk) GetChunk() []byte { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *ContentDeltaChunks_DeltaChunk) GetSizeDelta() uint32 { | ||||
| 	if m != nil && m.SizeDelta != nil { | ||||
| 		return *m.SizeDelta | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	proto.RegisterEnum("EContentDeltaChunkDataLocation", EContentDeltaChunkDataLocation_name, EContentDeltaChunkDataLocation_value) | ||||
| 	proto.RegisterType((*ContentManifestPayload)(nil), "ContentManifestPayload") | ||||
| 	proto.RegisterType((*ContentManifestPayload_FileMapping)(nil), "ContentManifestPayload.FileMapping") | ||||
| 	proto.RegisterType((*ContentManifestPayload_FileMapping_ChunkData)(nil), "ContentManifestPayload.FileMapping.ChunkData") | ||||
| @@ -510,37 +569,52 @@ func init() { | ||||
| func init() { proto.RegisterFile("content_manifest.proto", fileDescriptor_e3cda137a29253ba) } | ||||
|  | ||||
| var fileDescriptor_e3cda137a29253ba = []byte{ | ||||
| 	// 508 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcd, 0x8e, 0xd3, 0x30, | ||||
| 	0x10, 0xc7, 0x49, 0xbf, 0x3b, 0x6d, 0x50, 0xf1, 0xb2, 0x8b, 0x55, 0x24, 0x54, 0x95, 0x4b, 0x2f, | ||||
| 	0x9b, 0x03, 0x88, 0x23, 0x17, 0x76, 0x85, 0xb8, 0x54, 0x42, 0xe2, 0x01, 0x2c, 0xd7, 0x76, 0x53, | ||||
| 	0xab, 0x89, 0x1d, 0x6c, 0xe7, 0xb0, 0x9c, 0xb8, 0xf0, 0x18, 0xbc, 0x21, 0x12, 0xaf, 0x80, 0xec, | ||||
| 	0x38, 0x4d, 0x55, 0xf6, 0xb0, 0xb7, 0xcc, 0x47, 0x3c, 0xbf, 0xf9, 0xcf, 0x1f, 0x6e, 0x98, 0x56, | ||||
| 	0x4e, 0x28, 0x47, 0x4a, 0xaa, 0xe4, 0x5e, 0x58, 0x97, 0x55, 0x46, 0x3b, 0xbd, 0xfe, 0xdb, 0x83, | ||||
| 	0x9b, 0xbb, 0xa6, 0xb4, 0x8d, 0x95, 0xaf, 0xf4, 0xa1, 0xd0, 0x94, 0xa3, 0x0f, 0x30, 0x29, 0x69, | ||||
| 	0x55, 0x49, 0x95, 0x5b, 0x9c, 0xac, 0xfa, 0x9b, 0xd9, 0xbb, 0xb7, 0xd9, 0xe3, 0xad, 0xd9, 0x67, | ||||
| 	0x59, 0x88, 0x6d, 0xd3, 0xbb, 0xfc, 0xdd, 0x83, 0xd9, 0x59, 0x8c, 0x16, 0x30, 0xd9, 0xcb, 0x42, | ||||
| 	0x28, 0x5a, 0x0a, 0x9c, 0xac, 0x92, 0xcd, 0x14, 0xcd, 0x61, 0x60, 0xe5, 0x0f, 0x81, 0x7b, 0xab, | ||||
| 	0x64, 0x33, 0x40, 0x29, 0x0c, 0xf7, 0x05, 0xcd, 0x2d, 0xee, 0xaf, 0x92, 0x4d, 0x8a, 0x5e, 0xc2, | ||||
| 	0xdc, 0x1e, 0x28, 0x39, 0xfd, 0x32, 0x58, 0x25, 0x9b, 0x39, 0xba, 0x82, 0x99, 0xcf, 0xc6, 0x25, | ||||
| 	0xf0, 0x30, 0x24, 0x3f, 0xc2, 0x88, 0x1d, 0x6a, 0x75, 0xb4, 0x78, 0x14, 0xf0, 0x6e, 0x9f, 0x80, | ||||
| 	0x97, 0xdd, 0xf9, 0x3f, 0xee, 0xa9, 0xa3, 0x08, 0x01, 0x14, 0x52, 0x1d, 0x1d, 0x35, 0xb9, 0x70, | ||||
| 	0x78, 0xec, 0xd1, 0x96, 0x14, 0xa6, 0x5d, 0xc3, 0x0c, 0xfa, 0xf6, 0x40, 0x03, 0xf4, 0xdc, 0x07, | ||||
| 	0xcc, 0xb0, 0xc0, 0x3c, 0x46, 0xcf, 0x61, 0xa4, 0xf7, 0x7b, 0x2b, 0x5c, 0x80, 0x1e, 0x78, 0x3c, | ||||
| 	0xb6, 0x23, 0xda, 0xc8, 0x5c, 0x2a, 0x5a, 0x04, 0xe6, 0x14, 0x5d, 0x43, 0xca, 0x76, 0x84, 0xe9, | ||||
| 	0xb2, 0x32, 0xc2, 0x5a, 0xc1, 0x03, 0x75, 0xba, 0xfe, 0x93, 0xc0, 0xab, 0x0b, 0xce, 0xad, 0x70, | ||||
| 	0x94, 0xfb, 0x89, 0x0b, 0x98, 0x70, 0x51, 0x69, 0x47, 0x24, 0x0f, 0x63, 0x83, 0x1c, 0xb9, 0xe4, | ||||
| 	0xa7, 0xab, 0x45, 0xcd, 0xfc, 0xd3, 0x46, 0x50, 0x27, 0xb5, 0x22, 0x4e, 0x96, 0x22, 0x6a, 0xf7, | ||||
| 	0x1a, 0xae, 0x5a, 0xdd, 0x2c, 0x11, 0x8a, 0x99, 0x87, 0xca, 0x09, 0x1e, 0x70, 0x26, 0x08, 0xc3, | ||||
| 	0x82, 0xed, 0x08, 0x97, 0xf6, 0xd8, 0x81, 0x0e, 0xc3, 0x6b, 0x4b, 0x40, 0x6d, 0xe5, 0x8c, 0x76, | ||||
| 	0xd4, 0x4e, 0xaa, 0x95, 0xfc, 0x5e, 0x0b, 0x12, 0xa5, 0x1e, 0x9f, 0x76, 0x33, 0xec, 0x6c, 0xc6, | ||||
| 	0x24, 0xa4, 0x5f, 0xc0, 0xd4, 0xa7, 0x59, 0x21, 0xa8, 0xc1, 0xd3, 0xb0, 0xee, 0x2d, 0xe0, 0x8b, | ||||
| 	0x6d, 0xbf, 0xc9, 0x5c, 0x51, 0x57, 0x1b, 0xe1, 0xdb, 0x6d, 0x1b, 0x34, 0x32, 0xaf, 0x7f, 0xf5, | ||||
| 	0x00, 0xc5, 0xfe, 0x7b, 0x51, 0x38, 0x1a, 0xae, 0x61, 0x1f, 0x11, 0x66, 0x09, 0xa8, 0x15, 0x85, | ||||
| 	0x48, 0x4e, 0xac, 0xae, 0x0d, 0x6b, 0x2d, 0x75, 0x51, 0x8b, 0x17, 0x6e, 0x4e, 0xf5, 0x1e, 0x66, | ||||
| 	0xbc, 0x7b, 0x18, 0x0f, 0x82, 0x73, 0xde, 0x64, 0xff, 0xcf, 0xcc, 0xba, 0xef, 0x65, 0x05, 0xd0, | ||||
| 	0x45, 0xde, 0x38, 0xde, 0x8c, 0x71, 0x64, 0x63, 0x8f, 0x98, 0x8b, 0xa3, 0x7a, 0x21, 0x77, 0x0d, | ||||
| 	0xa9, 0xf7, 0x79, 0x27, 0xf7, 0xc9, 0xe1, 0x15, 0x75, 0xec, 0x40, 0x4a, 0xe1, 0x0e, 0x9a, 0x47, | ||||
| 	0xb7, 0xa4, 0x30, 0x0c, 0x0a, 0x37, 0xde, 0xfe, 0x34, 0xfc, 0x92, 0xfc, 0x4c, 0x9e, 0xfd, 0x0b, | ||||
| 	0x00, 0x00, 0xff, 0xff, 0x00, 0x92, 0x22, 0xd7, 0xb7, 0x03, 0x00, 0x00, | ||||
| 	// 742 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4f, 0x6f, 0xd3, 0x4e, | ||||
| 	0x10, 0xad, 0x9b, 0xff, 0x93, 0xa4, 0xca, 0x6f, 0xfb, 0x53, 0x31, 0x01, 0xda, 0x34, 0xbd, 0x44, | ||||
| 	0x55, 0x6b, 0x44, 0x4f, 0x88, 0x0b, 0x7f, 0xd2, 0x56, 0x54, 0x22, 0xa2, 0xda, 0xf6, 0xbe, 0xda, | ||||
| 	0xac, 0x37, 0xf1, 0x2a, 0x8e, 0x1d, 0xbc, 0x9b, 0x43, 0xcb, 0x05, 0x89, 0x33, 0x9c, 0xf8, 0x4e, | ||||
| 	0x7c, 0x17, 0xc4, 0x87, 0x40, 0xbb, 0x5e, 0xdb, 0x51, 0x8a, 0x0a, 0xdc, 0x3c, 0x33, 0x6f, 0xd6, | ||||
| 	0xef, 0xbd, 0x99, 0x5d, 0xd8, 0x61, 0x71, 0xa4, 0x78, 0xa4, 0xc8, 0x9c, 0x46, 0x62, 0xc2, 0xa5, | ||||
| 	0xf2, 0x16, 0x49, 0xac, 0xe2, 0xfe, 0x8f, 0x12, 0xec, 0x0c, 0xd3, 0xd2, 0xc8, 0x56, 0x2e, 0xe9, | ||||
| 	0x4d, 0x18, 0x53, 0x1f, 0xbd, 0x84, 0xfa, 0x9c, 0x2e, 0x16, 0x22, 0x9a, 0x4a, 0xd7, 0xe9, 0x95, | ||||
| 	0x06, 0xcd, 0x93, 0x03, 0xef, 0xf7, 0x50, 0xef, 0x5c, 0x84, 0x7c, 0x94, 0x62, 0x71, 0xde, 0xd4, | ||||
| 	0xfd, 0x5a, 0x82, 0xe6, 0x4a, 0x05, 0x75, 0xa1, 0x3e, 0x11, 0x21, 0x8f, 0xe8, 0x9c, 0xbb, 0x4e, | ||||
| 	0xcf, 0x19, 0x34, 0x70, 0x1e, 0x23, 0x04, 0x65, 0x29, 0x6e, 0xb9, 0xbb, 0xd9, 0x73, 0x06, 0x65, | ||||
| 	0x6c, 0xbe, 0xd1, 0xff, 0x50, 0x99, 0x84, 0x74, 0x2a, 0xdd, 0x52, 0xcf, 0x19, 0xb4, 0x71, 0x1a, | ||||
| 	0xa0, 0x7d, 0x68, 0xc9, 0x80, 0x92, 0xfc, 0xa4, 0x72, 0xcf, 0x19, 0xb4, 0x70, 0x53, 0x06, 0xf4, | ||||
| 	0x3c, 0x3b, 0x6c, 0x0f, 0x74, 0x48, 0xac, 0x64, 0xb7, 0x62, 0x10, 0x20, 0x03, 0x6a, 0xe9, 0xa3, | ||||
| 	0x33, 0xa8, 0xb2, 0x60, 0x19, 0xcd, 0xa4, 0x5b, 0x35, 0xc2, 0x8e, 0xff, 0x42, 0x98, 0x37, 0xd4, | ||||
| 	0x1d, 0xa7, 0x54, 0x51, 0x6c, 0x9b, 0xd1, 0x2e, 0x40, 0x28, 0xa2, 0x99, 0xa2, 0xc9, 0x94, 0x2b, | ||||
| 	0xb7, 0x66, 0x24, 0xad, 0x64, 0xba, 0x5f, 0x1c, 0x68, 0xe4, 0x5d, 0xa8, 0x03, 0x25, 0x19, 0x50, | ||||
| 	0xa3, 0xbc, 0x85, 0xf5, 0xa7, 0xce, 0xb0, 0x84, 0x19, 0xcd, 0x35, 0xac, 0x3f, 0xd1, 0x0e, 0x54, | ||||
| 	0xe3, 0xc9, 0x44, 0x72, 0x65, 0x34, 0x97, 0xb1, 0x8d, 0xb4, 0x22, 0x36, 0x26, 0x71, 0x22, 0xa6, | ||||
| 	0x22, 0xa2, 0xa1, 0xd1, 0xdc, 0xc6, 0xc0, 0xc6, 0xef, 0x6d, 0x06, 0x1d, 0x40, 0x9b, 0x8d, 0x09, | ||||
| 	0x8b, 0xe7, 0x8b, 0x84, 0x4b, 0xc9, 0x7d, 0x23, 0xba, 0x8d, 0x5b, 0x6c, 0x3c, 0xcc, 0x73, 0xfd, | ||||
| 	0x9f, 0x9b, 0xf0, 0x60, 0x4d, 0xe8, 0x88, 0x2b, 0xea, 0x6b, 0x76, 0x0f, 0xa1, 0xee, 0xf3, 0x45, | ||||
| 	0xac, 0x88, 0xf0, 0x0d, 0xc5, 0x36, 0xae, 0x99, 0xf8, 0xc2, 0xd7, 0x8e, 0x4f, 0x85, 0x9f, 0x6f, | ||||
| 	0x8e, 0x9d, 0x51, 0x73, 0x2a, 0xfc, 0xec, 0x14, 0xf3, 0xfb, 0x84, 0x53, 0x25, 0xe2, 0x88, 0x28, | ||||
| 	0x31, 0xe7, 0x76, 0x64, 0xad, 0x2c, 0x79, 0x2d, 0xe6, 0x1c, 0x3d, 0x85, 0xed, 0x6c, 0x6a, 0x92, | ||||
| 	0xf0, 0x88, 0x25, 0x37, 0x0b, 0xc5, 0x7d, 0x23, 0xa6, 0x8e, 0x51, 0x5e, 0x3a, 0xcb, 0x2a, 0x68, | ||||
| 	0x00, 0x1d, 0x36, 0x26, 0xbe, 0x90, 0xb3, 0x42, 0x7a, 0xc5, 0xfc, 0x7c, 0x8b, 0x8d, 0x4f, 0x85, | ||||
| 	0x9c, 0xe5, 0xf2, 0x8f, 0x00, 0x65, 0xc8, 0x15, 0x0f, 0xaa, 0x06, 0xdb, 0x49, 0xb1, 0x85, 0x0f, | ||||
| 	0x9a, 0xed, 0x32, 0x12, 0x1f, 0x96, 0x9c, 0xd8, 0x2d, 0xa8, 0xa5, 0x6c, 0xd3, 0xe4, 0x30, 0x1d, | ||||
| 	0xae, 0x91, 0xc4, 0x56, 0x78, 0xd6, 0x33, 0x49, 0xac, 0x60, 0xf8, 0x08, 0x1a, 0x1a, 0xc4, 0x42, | ||||
| 	0x4e, 0x13, 0xb7, 0x61, 0x00, 0x75, 0x96, 0xb0, 0xa1, 0x8e, 0xfb, 0xcf, 0xc1, 0x5d, 0x73, 0xfb, | ||||
| 	0x4a, 0x4c, 0x23, 0xaa, 0x96, 0x09, 0x47, 0x8f, 0xa1, 0x21, 0xb3, 0xc0, 0xae, 0x44, 0x91, 0xe8, | ||||
| 	0x7f, 0x2b, 0x03, 0xb2, 0xad, 0xa7, 0x3c, 0x54, 0xd4, 0x52, 0xba, 0x67, 0x46, 0x47, 0x80, 0xb2, | ||||
| 	0xf9, 0x10, 0xe1, 0x13, 0x19, 0x2f, 0x13, 0x96, 0xdd, 0xa6, 0x4e, 0x56, 0xb9, 0xf0, 0xaf, 0x4c, | ||||
| 	0x7e, 0x1d, 0x6d, 0x17, 0xb8, 0xb4, 0x8e, 0xbe, 0x36, 0x79, 0xf4, 0x0a, 0x9a, 0x7e, 0xc1, 0xc2, | ||||
| 	0x2d, 0x9b, 0x2b, 0xb3, 0xeb, 0xdd, 0x25, 0xe8, 0x15, 0xdf, 0x78, 0xb5, 0x05, 0x7d, 0x84, 0x6d, | ||||
| 	0xe3, 0x34, 0xd1, 0xab, 0x46, 0xc2, 0x98, 0x99, 0xa5, 0x30, 0xb3, 0xdc, 0x3a, 0xd9, 0xf3, 0xce, | ||||
| 	0xee, 0x1c, 0xa5, 0x2f, 0xcc, 0x3b, 0x0b, 0x7b, 0x71, 0x38, 0x23, 0xf7, 0x23, 0x2e, 0xa2, 0x4b, | ||||
| 	0xfd, 0x98, 0x8d, 0x97, 0x13, 0xfc, 0x1f, 0x5b, 0x2f, 0x76, 0xbf, 0x3b, 0x00, 0x45, 0x1f, 0x7a, | ||||
| 	0x02, 0xfa, 0x25, 0xc8, 0x1c, 0xca, 0xac, 0x0f, 0xa8, 0xb5, 0xc6, 0x96, 0xad, 0x25, 0x9b, 0x79, | ||||
| 	0xd9, 0x7a, 0x71, 0x00, 0x6d, 0xfd, 0x36, 0x15, 0xfb, 0x68, 0x17, 0x5d, 0x27, 0xf3, 0x6d, 0xdc, | ||||
| 	0x87, 0xd6, 0x82, 0x2a, 0x16, 0x90, 0x39, 0x57, 0x41, 0xec, 0xdb, 0xeb, 0xda, 0x34, 0xb9, 0x91, | ||||
| 	0x49, 0xe9, 0xb7, 0xcd, 0x30, 0xb5, 0x8f, 0x53, 0x1a, 0x98, 0x9f, 0xeb, 0xd3, 0x8d, 0x77, 0x66, | ||||
| 	0x7d, 0xdb, 0x7a, 0x2d, 0x6e, 0xb9, 0xe1, 0x7f, 0xf8, 0xd9, 0x81, 0xdd, 0xfb, 0x9d, 0x40, 0x1e, | ||||
| 	0xfc, 0x83, 0x5b, 0x9d, 0x0d, 0xf4, 0x0c, 0x8e, 0xff, 0x84, 0x7f, 0x3d, 0x51, 0x3c, 0xc9, 0x5b, | ||||
| 	0x9c, 0x37, 0x95, 0xb7, 0xce, 0x27, 0x67, 0xe3, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x9c, | ||||
| 	0xf1, 0x49, 0x52, 0x06, 0x00, 0x00, | ||||
| } | ||||
|   | ||||
							
								
								
									
										669
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/steamlang/enums.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										669
									
								
								vendor/github.com/Philipp15b/go-steam/protocol/steamlang/enums.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -400,6 +400,7 @@ const ( | ||||
| 	EMsg_FBSBootstrapperGetPackageChunkResponse                   EMsg = 1131 | ||||
| 	EMsg_FBSBootstrapperPackageTransferProgress                   EMsg = 1132 | ||||
| 	EMsg_FBSRestartBootstrapper                                   EMsg = 1133 | ||||
| 	EMsg_FBSPauseFrozenDumps                                      EMsg = 1134 | ||||
| 	EMsg_BaseFileXfer                                             EMsg = 1200 | ||||
| 	EMsg_FileXferRequest                                          EMsg = 1200 | ||||
| 	EMsg_FileXferResponse                                         EMsg = 1201 | ||||
| @@ -416,6 +417,8 @@ const ( | ||||
| 	EMsg_BaseBS                                                   EMsg = 1400 | ||||
| 	EMsg_BSPurchaseStart                                          EMsg = 1401 | ||||
| 	EMsg_BSPurchaseResponse                                       EMsg = 1402 | ||||
| 	EMsg_BSAuthenticateCCTrans                                    EMsg = 1403 | ||||
| 	EMsg_BSAuthenticateCCTransResponse                            EMsg = 1404 | ||||
| 	EMsg_BSSettleComplete                                         EMsg = 1406 | ||||
| 	EMsg_BSInitPayPalTxn                                          EMsg = 1408 | ||||
| 	EMsg_BSInitPayPalTxnResponse                                  EMsg = 1409 | ||||
| @@ -979,6 +982,8 @@ const ( | ||||
| 	EMsg_AMFundedPayment                                          EMsg = 4418 | ||||
| 	EMsg_AMFundedPaymentResponse                                  EMsg = 4419 | ||||
| 	EMsg_AMRequestPersonaUpdateForChatServer                      EMsg = 4420 | ||||
| 	EMsg_AMPerfectWorldPayment                                    EMsg = 4421 | ||||
| 	EMsg_AMPerfectWorldPaymentResponse                            EMsg = 4422 | ||||
| 	EMsg_BasePSRange                                              EMsg = 5000 | ||||
| 	EMsg_PSCreateShoppingCart                                     EMsg = 5001 | ||||
| 	EMsg_PSCreateShoppingCartResponse                             EMsg = 5002 | ||||
| @@ -1238,6 +1243,9 @@ const ( | ||||
| 	EMsg_ClientNetworkingCertRequestResponse                      EMsg = 5622 | ||||
| 	EMsg_ClientChallengeRequest                                   EMsg = 5623 | ||||
| 	EMsg_ClientChallengeResponse                                  EMsg = 5624 | ||||
| 	EMsg_BadgeCraftedNotification                                 EMsg = 5625 | ||||
| 	EMsg_ClientNetworkingMobileCertRequest                        EMsg = 5626 | ||||
| 	EMsg_ClientNetworkingMobileCertRequestResponse                EMsg = 5627 | ||||
| 	EMsg_BaseMDS                                                  EMsg = 5800 | ||||
| 	EMsg_AMToMDSGetDepotDecryptionKey                             EMsg = 5812 | ||||
| 	EMsg_MDSToAMGetDepotDecryptionKeyResponse                     EMsg = 5813 | ||||
| @@ -1303,6 +1311,8 @@ const ( | ||||
| 	EMsg_ClientMMSSetRatelimitPolicyOnClient                      EMsg = 6625 | ||||
| 	EMsg_ClientMMSGetLobbyStatus                                  EMsg = 6626 | ||||
| 	EMsg_ClientMMSGetLobbyStatusResponse                          EMsg = 6627 | ||||
| 	EMsg_MMSGetLobbyList                                          EMsg = 6628 | ||||
| 	EMsg_MMSGetLobbyListResponse                                  EMsg = 6629 | ||||
| 	EMsg_NonStdMsgBase                                            EMsg = 6800 | ||||
| 	EMsg_NonStdMsgMemcached                                       EMsg = 6801 | ||||
| 	EMsg_NonStdMsgHTTPServer                                      EMsg = 6802 | ||||
| @@ -1318,6 +1328,7 @@ const ( | ||||
| 	EMsg_NonStdMsgSteam2Emulator                                  EMsg = 6812 | ||||
| 	EMsg_NonStdMsgRTMPServer                                      EMsg = 6813 | ||||
| 	EMsg_NonStdMsgWebSocket                                       EMsg = 6814 | ||||
| 	EMsg_NonStdMsgRedis                                           EMsg = 6815 | ||||
| 	EMsg_UDSBase                                                  EMsg = 7000 | ||||
| 	EMsg_ClientUDSP2PSessionStarted                               EMsg = 7001 | ||||
| 	EMsg_ClientUDSP2PSessionEnded                                 EMsg = 7002 | ||||
| @@ -2072,6 +2083,7 @@ var EMsg_name = map[EMsg]string{ | ||||
| 	1131:  "EMsg_FBSBootstrapperGetPackageChunkResponse", | ||||
| 	1132:  "EMsg_FBSBootstrapperPackageTransferProgress", | ||||
| 	1133:  "EMsg_FBSRestartBootstrapper", | ||||
| 	1134:  "EMsg_FBSPauseFrozenDumps", | ||||
| 	1200:  "EMsg_BaseFileXfer", | ||||
| 	1201:  "EMsg_FileXferResponse", | ||||
| 	1202:  "EMsg_FileXferData", | ||||
| @@ -2086,7 +2098,8 @@ var EMsg_name = map[EMsg]string{ | ||||
| 	1400:  "EMsg_BaseBS", | ||||
| 	1401:  "EMsg_BSPurchaseStart", | ||||
| 	1402:  "EMsg_BSPurchaseResponse", | ||||
| 	1404:  "EMsg_BSSettleNOVA", | ||||
| 	1403:  "EMsg_BSAuthenticateCCTrans", | ||||
| 	1404:  "EMsg_BSAuthenticateCCTransResponse", | ||||
| 	1406:  "EMsg_BSSettleComplete", | ||||
| 	1407:  "EMsg_BSBannedRequest", | ||||
| 	1408:  "EMsg_BSInitPayPalTxn", | ||||
| @@ -2772,6 +2785,8 @@ var EMsg_name = map[EMsg]string{ | ||||
| 	4418:  "EMsg_AMFundedPayment", | ||||
| 	4419:  "EMsg_AMFundedPaymentResponse", | ||||
| 	4420:  "EMsg_AMRequestPersonaUpdateForChatServer", | ||||
| 	4421:  "EMsg_AMPerfectWorldPayment", | ||||
| 	4422:  "EMsg_AMPerfectWorldPaymentResponse", | ||||
| 	5000:  "EMsg_BasePSRange", | ||||
| 	5001:  "EMsg_PSCreateShoppingCart", | ||||
| 	5002:  "EMsg_PSCreateShoppingCartResponse", | ||||
| @@ -3041,6 +3056,9 @@ var EMsg_name = map[EMsg]string{ | ||||
| 	5622:  "EMsg_ClientNetworkingCertRequestResponse", | ||||
| 	5623:  "EMsg_ClientChallengeRequest", | ||||
| 	5624:  "EMsg_ClientChallengeResponse", | ||||
| 	5625:  "EMsg_BadgeCraftedNotification", | ||||
| 	5626:  "EMsg_ClientNetworkingMobileCertRequest", | ||||
| 	5627:  "EMsg_ClientNetworkingMobileCertRequestResponse", | ||||
| 	5800:  "EMsg_BaseMDS", | ||||
| 	5801:  "EMsg_ClientMDSLoginRequest", | ||||
| 	5802:  "EMsg_ClientMDSLoginResponse", | ||||
| @@ -3136,6 +3154,8 @@ var EMsg_name = map[EMsg]string{ | ||||
| 	6625:  "EMsg_ClientMMSSetRatelimitPolicyOnClient", | ||||
| 	6626:  "EMsg_ClientMMSGetLobbyStatus", | ||||
| 	6627:  "EMsg_ClientMMSGetLobbyStatusResponse", | ||||
| 	6628:  "EMsg_MMSGetLobbyList", | ||||
| 	6629:  "EMsg_MMSGetLobbyListResponse", | ||||
| 	6800:  "EMsg_NonStdMsgBase", | ||||
| 	6801:  "EMsg_NonStdMsgMemcached", | ||||
| 	6802:  "EMsg_NonStdMsgHTTPServer", | ||||
| @@ -3151,6 +3171,7 @@ var EMsg_name = map[EMsg]string{ | ||||
| 	6812:  "EMsg_NonStdMsgSteam2Emulator", | ||||
| 	6813:  "EMsg_NonStdMsgRTMPServer", | ||||
| 	6814:  "EMsg_NonStdMsgWebSocket", | ||||
| 	6815:  "EMsg_NonStdMsgRedis", | ||||
| 	7000:  "EMsg_UDSBase", | ||||
| 	7001:  "EMsg_ClientUDSP2PSessionStarted", | ||||
| 	7002:  "EMsg_ClientUDSP2PSessionEnded", | ||||
| @@ -3487,119 +3508,122 @@ func (e EMsg) String() string { | ||||
| type EResult int32 | ||||
|  | ||||
| const ( | ||||
| 	EResult_Invalid                                 EResult = 0 | ||||
| 	EResult_OK                                      EResult = 1 | ||||
| 	EResult_Fail                                    EResult = 2 | ||||
| 	EResult_NoConnection                            EResult = 3 | ||||
| 	EResult_InvalidPassword                         EResult = 5 | ||||
| 	EResult_LoggedInElsewhere                       EResult = 6 | ||||
| 	EResult_InvalidProtocolVer                      EResult = 7 | ||||
| 	EResult_InvalidParam                            EResult = 8 | ||||
| 	EResult_FileNotFound                            EResult = 9 | ||||
| 	EResult_Busy                                    EResult = 10 | ||||
| 	EResult_InvalidState                            EResult = 11 | ||||
| 	EResult_InvalidName                             EResult = 12 | ||||
| 	EResult_InvalidEmail                            EResult = 13 | ||||
| 	EResult_DuplicateName                           EResult = 14 | ||||
| 	EResult_AccessDenied                            EResult = 15 | ||||
| 	EResult_Timeout                                 EResult = 16 | ||||
| 	EResult_Banned                                  EResult = 17 | ||||
| 	EResult_AccountNotFound                         EResult = 18 | ||||
| 	EResult_InvalidSteamID                          EResult = 19 | ||||
| 	EResult_ServiceUnavailable                      EResult = 20 | ||||
| 	EResult_NotLoggedOn                             EResult = 21 | ||||
| 	EResult_Pending                                 EResult = 22 | ||||
| 	EResult_EncryptionFailure                       EResult = 23 | ||||
| 	EResult_InsufficientPrivilege                   EResult = 24 | ||||
| 	EResult_LimitExceeded                           EResult = 25 | ||||
| 	EResult_Revoked                                 EResult = 26 | ||||
| 	EResult_Expired                                 EResult = 27 | ||||
| 	EResult_AlreadyRedeemed                         EResult = 28 | ||||
| 	EResult_DuplicateRequest                        EResult = 29 | ||||
| 	EResult_AlreadyOwned                            EResult = 30 | ||||
| 	EResult_IPNotFound                              EResult = 31 | ||||
| 	EResult_PersistFailed                           EResult = 32 | ||||
| 	EResult_LockingFailed                           EResult = 33 | ||||
| 	EResult_LogonSessionReplaced                    EResult = 34 | ||||
| 	EResult_ConnectFailed                           EResult = 35 | ||||
| 	EResult_HandshakeFailed                         EResult = 36 | ||||
| 	EResult_IOFailure                               EResult = 37 | ||||
| 	EResult_RemoteDisconnect                        EResult = 38 | ||||
| 	EResult_ShoppingCartNotFound                    EResult = 39 | ||||
| 	EResult_Blocked                                 EResult = 40 | ||||
| 	EResult_Ignored                                 EResult = 41 | ||||
| 	EResult_NoMatch                                 EResult = 42 | ||||
| 	EResult_AccountDisabled                         EResult = 43 | ||||
| 	EResult_ServiceReadOnly                         EResult = 44 | ||||
| 	EResult_AccountNotFeatured                      EResult = 45 | ||||
| 	EResult_AdministratorOK                         EResult = 46 | ||||
| 	EResult_ContentVersion                          EResult = 47 | ||||
| 	EResult_TryAnotherCM                            EResult = 48 | ||||
| 	EResult_PasswordRequiredToKickSession           EResult = 49 | ||||
| 	EResult_AlreadyLoggedInElsewhere                EResult = 50 | ||||
| 	EResult_Suspended                               EResult = 51 | ||||
| 	EResult_Cancelled                               EResult = 52 | ||||
| 	EResult_DataCorruption                          EResult = 53 | ||||
| 	EResult_DiskFull                                EResult = 54 | ||||
| 	EResult_RemoteCallFailed                        EResult = 55 | ||||
| 	EResult_PasswordUnset                           EResult = 56 | ||||
| 	EResult_ExternalAccountUnlinked                 EResult = 57 | ||||
| 	EResult_PSNTicketInvalid                        EResult = 58 | ||||
| 	EResult_ExternalAccountAlreadyLinked            EResult = 59 | ||||
| 	EResult_RemoteFileConflict                      EResult = 60 | ||||
| 	EResult_IllegalPassword                         EResult = 61 | ||||
| 	EResult_SameAsPreviousValue                     EResult = 62 | ||||
| 	EResult_AccountLogonDenied                      EResult = 63 | ||||
| 	EResult_CannotUseOldPassword                    EResult = 64 | ||||
| 	EResult_InvalidLoginAuthCode                    EResult = 65 | ||||
| 	EResult_AccountLogonDeniedNoMail                EResult = 66 | ||||
| 	EResult_HardwareNotCapableOfIPT                 EResult = 67 | ||||
| 	EResult_IPTInitError                            EResult = 68 | ||||
| 	EResult_ParentalControlRestricted               EResult = 69 | ||||
| 	EResult_FacebookQueryError                      EResult = 70 | ||||
| 	EResult_ExpiredLoginAuthCode                    EResult = 71 | ||||
| 	EResult_IPLoginRestrictionFailed                EResult = 72 | ||||
| 	EResult_AccountLockedDown                       EResult = 73 | ||||
| 	EResult_AccountLogonDeniedVerifiedEmailRequired EResult = 74 | ||||
| 	EResult_NoMatchingURL                           EResult = 75 | ||||
| 	EResult_BadResponse                             EResult = 76 | ||||
| 	EResult_RequirePasswordReEntry                  EResult = 77 | ||||
| 	EResult_ValueOutOfRange                         EResult = 78 | ||||
| 	EResult_UnexpectedError                         EResult = 79 | ||||
| 	EResult_Disabled                                EResult = 80 | ||||
| 	EResult_InvalidCEGSubmission                    EResult = 81 | ||||
| 	EResult_RestrictedDevice                        EResult = 82 | ||||
| 	EResult_RegionLocked                            EResult = 83 | ||||
| 	EResult_RateLimitExceeded                       EResult = 84 | ||||
| 	EResult_AccountLoginDeniedNeedTwoFactor         EResult = 85 | ||||
| 	EResult_ItemDeleted                             EResult = 86 | ||||
| 	EResult_AccountLoginDeniedThrottle              EResult = 87 | ||||
| 	EResult_TwoFactorCodeMismatch                   EResult = 88 | ||||
| 	EResult_TwoFactorActivationCodeMismatch         EResult = 89 | ||||
| 	EResult_AccountAssociatedToMultiplePartners     EResult = 90 | ||||
| 	EResult_NotModified                             EResult = 91 | ||||
| 	EResult_NoMobileDevice                          EResult = 92 | ||||
| 	EResult_TimeNotSynced                           EResult = 93 | ||||
| 	EResult_SMSCodeFailed                           EResult = 94 | ||||
| 	EResult_AccountLimitExceeded                    EResult = 95 | ||||
| 	EResult_AccountActivityLimitExceeded            EResult = 96 | ||||
| 	EResult_PhoneActivityLimitExceeded              EResult = 97 | ||||
| 	EResult_RefundToWallet                          EResult = 98 | ||||
| 	EResult_EmailSendFailure                        EResult = 99 | ||||
| 	EResult_NotSettled                              EResult = 100 | ||||
| 	EResult_NeedCaptcha                             EResult = 101 | ||||
| 	EResult_GSLTDenied                              EResult = 102 | ||||
| 	EResult_GSOwnerDenied                           EResult = 103 | ||||
| 	EResult_InvalidItemType                         EResult = 104 | ||||
| 	EResult_IPBanned                                EResult = 105 | ||||
| 	EResult_GSLTExpired                             EResult = 106 | ||||
| 	EResult_InsufficientFunds                       EResult = 107 | ||||
| 	EResult_TooManyPending                          EResult = 108 | ||||
| 	EResult_NoSiteLicensesFound                     EResult = 109 | ||||
| 	EResult_WGNetworkSendExceeded                   EResult = 110 | ||||
| 	EResult_AccountNotFriends                       EResult = 111 | ||||
| 	EResult_LimitedUserAccount                      EResult = 112 | ||||
| 	EResult_CantRemoveItem                          EResult = 113 | ||||
| 	EResult_Invalid                                  EResult = 0 | ||||
| 	EResult_OK                                       EResult = 1 | ||||
| 	EResult_Fail                                     EResult = 2 | ||||
| 	EResult_NoConnection                             EResult = 3 | ||||
| 	EResult_InvalidPassword                          EResult = 5 | ||||
| 	EResult_LoggedInElsewhere                        EResult = 6 | ||||
| 	EResult_InvalidProtocolVer                       EResult = 7 | ||||
| 	EResult_InvalidParam                             EResult = 8 | ||||
| 	EResult_FileNotFound                             EResult = 9 | ||||
| 	EResult_Busy                                     EResult = 10 | ||||
| 	EResult_InvalidState                             EResult = 11 | ||||
| 	EResult_InvalidName                              EResult = 12 | ||||
| 	EResult_InvalidEmail                             EResult = 13 | ||||
| 	EResult_DuplicateName                            EResult = 14 | ||||
| 	EResult_AccessDenied                             EResult = 15 | ||||
| 	EResult_Timeout                                  EResult = 16 | ||||
| 	EResult_Banned                                   EResult = 17 | ||||
| 	EResult_AccountNotFound                          EResult = 18 | ||||
| 	EResult_InvalidSteamID                           EResult = 19 | ||||
| 	EResult_ServiceUnavailable                       EResult = 20 | ||||
| 	EResult_NotLoggedOn                              EResult = 21 | ||||
| 	EResult_Pending                                  EResult = 22 | ||||
| 	EResult_EncryptionFailure                        EResult = 23 | ||||
| 	EResult_InsufficientPrivilege                    EResult = 24 | ||||
| 	EResult_LimitExceeded                            EResult = 25 | ||||
| 	EResult_Revoked                                  EResult = 26 | ||||
| 	EResult_Expired                                  EResult = 27 | ||||
| 	EResult_AlreadyRedeemed                          EResult = 28 | ||||
| 	EResult_DuplicateRequest                         EResult = 29 | ||||
| 	EResult_AlreadyOwned                             EResult = 30 | ||||
| 	EResult_IPNotFound                               EResult = 31 | ||||
| 	EResult_PersistFailed                            EResult = 32 | ||||
| 	EResult_LockingFailed                            EResult = 33 | ||||
| 	EResult_LogonSessionReplaced                     EResult = 34 | ||||
| 	EResult_ConnectFailed                            EResult = 35 | ||||
| 	EResult_HandshakeFailed                          EResult = 36 | ||||
| 	EResult_IOFailure                                EResult = 37 | ||||
| 	EResult_RemoteDisconnect                         EResult = 38 | ||||
| 	EResult_ShoppingCartNotFound                     EResult = 39 | ||||
| 	EResult_Blocked                                  EResult = 40 | ||||
| 	EResult_Ignored                                  EResult = 41 | ||||
| 	EResult_NoMatch                                  EResult = 42 | ||||
| 	EResult_AccountDisabled                          EResult = 43 | ||||
| 	EResult_ServiceReadOnly                          EResult = 44 | ||||
| 	EResult_AccountNotFeatured                       EResult = 45 | ||||
| 	EResult_AdministratorOK                          EResult = 46 | ||||
| 	EResult_ContentVersion                           EResult = 47 | ||||
| 	EResult_TryAnotherCM                             EResult = 48 | ||||
| 	EResult_PasswordRequiredToKickSession            EResult = 49 | ||||
| 	EResult_AlreadyLoggedInElsewhere                 EResult = 50 | ||||
| 	EResult_Suspended                                EResult = 51 | ||||
| 	EResult_Cancelled                                EResult = 52 | ||||
| 	EResult_DataCorruption                           EResult = 53 | ||||
| 	EResult_DiskFull                                 EResult = 54 | ||||
| 	EResult_RemoteCallFailed                         EResult = 55 | ||||
| 	EResult_PasswordUnset                            EResult = 56 | ||||
| 	EResult_ExternalAccountUnlinked                  EResult = 57 | ||||
| 	EResult_PSNTicketInvalid                         EResult = 58 | ||||
| 	EResult_ExternalAccountAlreadyLinked             EResult = 59 | ||||
| 	EResult_RemoteFileConflict                       EResult = 60 | ||||
| 	EResult_IllegalPassword                          EResult = 61 | ||||
| 	EResult_SameAsPreviousValue                      EResult = 62 | ||||
| 	EResult_AccountLogonDenied                       EResult = 63 | ||||
| 	EResult_CannotUseOldPassword                     EResult = 64 | ||||
| 	EResult_InvalidLoginAuthCode                     EResult = 65 | ||||
| 	EResult_AccountLogonDeniedNoMail                 EResult = 66 | ||||
| 	EResult_HardwareNotCapableOfIPT                  EResult = 67 | ||||
| 	EResult_IPTInitError                             EResult = 68 | ||||
| 	EResult_ParentalControlRestricted                EResult = 69 | ||||
| 	EResult_FacebookQueryError                       EResult = 70 | ||||
| 	EResult_ExpiredLoginAuthCode                     EResult = 71 | ||||
| 	EResult_IPLoginRestrictionFailed                 EResult = 72 | ||||
| 	EResult_AccountLockedDown                        EResult = 73 | ||||
| 	EResult_AccountLogonDeniedVerifiedEmailRequired  EResult = 74 | ||||
| 	EResult_NoMatchingURL                            EResult = 75 | ||||
| 	EResult_BadResponse                              EResult = 76 | ||||
| 	EResult_RequirePasswordReEntry                   EResult = 77 | ||||
| 	EResult_ValueOutOfRange                          EResult = 78 | ||||
| 	EResult_UnexpectedError                          EResult = 79 | ||||
| 	EResult_Disabled                                 EResult = 80 | ||||
| 	EResult_InvalidCEGSubmission                     EResult = 81 | ||||
| 	EResult_RestrictedDevice                         EResult = 82 | ||||
| 	EResult_RegionLocked                             EResult = 83 | ||||
| 	EResult_RateLimitExceeded                        EResult = 84 | ||||
| 	EResult_AccountLoginDeniedNeedTwoFactor          EResult = 85 | ||||
| 	EResult_ItemDeleted                              EResult = 86 | ||||
| 	EResult_AccountLoginDeniedThrottle               EResult = 87 | ||||
| 	EResult_TwoFactorCodeMismatch                    EResult = 88 | ||||
| 	EResult_TwoFactorActivationCodeMismatch          EResult = 89 | ||||
| 	EResult_AccountAssociatedToMultiplePartners      EResult = 90 | ||||
| 	EResult_NotModified                              EResult = 91 | ||||
| 	EResult_NoMobileDevice                           EResult = 92 | ||||
| 	EResult_TimeNotSynced                            EResult = 93 | ||||
| 	EResult_SMSCodeFailed                            EResult = 94 | ||||
| 	EResult_AccountLimitExceeded                     EResult = 95 | ||||
| 	EResult_AccountActivityLimitExceeded             EResult = 96 | ||||
| 	EResult_PhoneActivityLimitExceeded               EResult = 97 | ||||
| 	EResult_RefundToWallet                           EResult = 98 | ||||
| 	EResult_EmailSendFailure                         EResult = 99 | ||||
| 	EResult_NotSettled                               EResult = 100 | ||||
| 	EResult_NeedCaptcha                              EResult = 101 | ||||
| 	EResult_GSLTDenied                               EResult = 102 | ||||
| 	EResult_GSOwnerDenied                            EResult = 103 | ||||
| 	EResult_InvalidItemType                          EResult = 104 | ||||
| 	EResult_IPBanned                                 EResult = 105 | ||||
| 	EResult_GSLTExpired                              EResult = 106 | ||||
| 	EResult_InsufficientFunds                        EResult = 107 | ||||
| 	EResult_TooManyPending                           EResult = 108 | ||||
| 	EResult_NoSiteLicensesFound                      EResult = 109 | ||||
| 	EResult_WGNetworkSendExceeded                    EResult = 110 | ||||
| 	EResult_AccountNotFriends                        EResult = 111 | ||||
| 	EResult_LimitedUserAccount                       EResult = 112 | ||||
| 	EResult_CantRemoveItem                           EResult = 113 | ||||
| 	EResult_AccountHasBeenDeleted                    EResult = 114 | ||||
| 	EResult_AccountHasAnExistingUserCancelledLicense EResult = 115 | ||||
| 	EResult_DeniedDueToCommunityCooldown             EResult = 116 | ||||
| ) | ||||
|  | ||||
| var EResult_name = map[EResult]string{ | ||||
| @@ -3716,6 +3740,9 @@ var EResult_name = map[EResult]string{ | ||||
| 	111: "EResult_AccountNotFriends", | ||||
| 	112: "EResult_LimitedUserAccount", | ||||
| 	113: "EResult_CantRemoveItem", | ||||
| 	114: "EResult_AccountHasBeenDeleted", | ||||
| 	115: "EResult_AccountHasAnExistingUserCancelledLicense", | ||||
| 	116: "EResult_DeniedDueToCommunityCooldown", | ||||
| } | ||||
|  | ||||
| func (e EResult) String() string { | ||||
| @@ -3743,7 +3770,6 @@ const ( | ||||
| 	EUniverse_Beta     EUniverse = 2 | ||||
| 	EUniverse_Internal EUniverse = 3 | ||||
| 	EUniverse_Dev      EUniverse = 4 | ||||
| 	EUniverse_Max      EUniverse = 5 | ||||
| ) | ||||
|  | ||||
| var EUniverse_name = map[EUniverse]string{ | ||||
| @@ -3752,7 +3778,6 @@ var EUniverse_name = map[EUniverse]string{ | ||||
| 	2: "EUniverse_Beta", | ||||
| 	3: "EUniverse_Internal", | ||||
| 	4: "EUniverse_Dev", | ||||
| 	5: "EUniverse_Max", | ||||
| } | ||||
|  | ||||
| func (e EUniverse) String() string { | ||||
| @@ -3836,7 +3861,6 @@ const ( | ||||
| 	EPersonaState_LookingToTrade EPersonaState = 5 | ||||
| 	EPersonaState_LookingToPlay  EPersonaState = 6 | ||||
| 	EPersonaState_Invisible      EPersonaState = 7 | ||||
| 	EPersonaState_Max            EPersonaState = 8 | ||||
| ) | ||||
|  | ||||
| var EPersonaState_name = map[EPersonaState]string{ | ||||
| @@ -3848,7 +3872,6 @@ var EPersonaState_name = map[EPersonaState]string{ | ||||
| 	5: "EPersonaState_LookingToTrade", | ||||
| 	6: "EPersonaState_LookingToPlay", | ||||
| 	7: "EPersonaState_Invisible", | ||||
| 	8: "EPersonaState_Max", | ||||
| } | ||||
|  | ||||
| func (e EPersonaState) String() string { | ||||
| @@ -3882,7 +3905,6 @@ const ( | ||||
| 	EAccountType_Chat           EAccountType = 8 | ||||
| 	EAccountType_ConsoleUser    EAccountType = 9 | ||||
| 	EAccountType_AnonUser       EAccountType = 10 | ||||
| 	EAccountType_Max            EAccountType = 11 | ||||
| ) | ||||
|  | ||||
| var EAccountType_name = map[EAccountType]string{ | ||||
| @@ -3897,7 +3919,6 @@ var EAccountType_name = map[EAccountType]string{ | ||||
| 	8:  "EAccountType_Chat", | ||||
| 	9:  "EAccountType_ConsoleUser", | ||||
| 	10: "EAccountType_AnonUser", | ||||
| 	11: "EAccountType_Max", | ||||
| } | ||||
|  | ||||
| func (e EAccountType) String() string { | ||||
| @@ -3927,7 +3948,6 @@ const ( | ||||
| 	EFriendRelationship_RequestInitiator EFriendRelationship = 4 | ||||
| 	EFriendRelationship_Ignored          EFriendRelationship = 5 | ||||
| 	EFriendRelationship_IgnoredFriend    EFriendRelationship = 6 | ||||
| 	EFriendRelationship_Max              EFriendRelationship = 8 | ||||
| ) | ||||
|  | ||||
| var EFriendRelationship_name = map[EFriendRelationship]string{ | ||||
| @@ -3939,7 +3959,6 @@ var EFriendRelationship_name = map[EFriendRelationship]string{ | ||||
| 	5: "EFriendRelationship_Ignored", | ||||
| 	6: "EFriendRelationship_IgnoredFriend", | ||||
| 	7: "EFriendRelationship_SuggestedFriend", | ||||
| 	8: "EFriendRelationship_Max", | ||||
| } | ||||
|  | ||||
| func (e EFriendRelationship) String() string { | ||||
| @@ -4212,6 +4231,7 @@ const ( | ||||
| 	EPersonaStateFlag_HasRichPresence      EPersonaStateFlag = 1 | ||||
| 	EPersonaStateFlag_InJoinableGame       EPersonaStateFlag = 2 | ||||
| 	EPersonaStateFlag_Golden               EPersonaStateFlag = 4 | ||||
| 	EPersonaStateFlag_RemotePlayTogether   EPersonaStateFlag = 8 | ||||
| 	EPersonaStateFlag_ClientTypeWeb        EPersonaStateFlag = 256 | ||||
| 	EPersonaStateFlag_ClientTypeMobile     EPersonaStateFlag = 512 | ||||
| 	EPersonaStateFlag_ClientTypeTenfoot    EPersonaStateFlag = 1024 | ||||
| @@ -4224,6 +4244,7 @@ var EPersonaStateFlag_name = map[EPersonaStateFlag]string{ | ||||
| 	1:    "EPersonaStateFlag_HasRichPresence", | ||||
| 	2:    "EPersonaStateFlag_InJoinableGame", | ||||
| 	4:    "EPersonaStateFlag_Golden", | ||||
| 	8:    "EPersonaStateFlag_RemotePlayTogether", | ||||
| 	256:  "EPersonaStateFlag_OnlineUsingWeb", | ||||
| 	512:  "EPersonaStateFlag_OnlineUsingMobile", | ||||
| 	1024: "EPersonaStateFlag_OnlineUsingBigPicture", | ||||
| @@ -4524,6 +4545,7 @@ const ( | ||||
| 	EPaymentMethod_Valve                  EPaymentMethod = 129 | ||||
| 	EPaymentMethod_MasterComp             EPaymentMethod = 130 | ||||
| 	EPaymentMethod_Promotional            EPaymentMethod = 131 | ||||
| 	EPaymentMethod_MasterSubscription     EPaymentMethod = 134 | ||||
| 	EPaymentMethod_OEMTicket              EPaymentMethod = 256 | ||||
| 	EPaymentMethod_Split                  EPaymentMethod = 512 | ||||
| 	EPaymentMethod_Complimentary          EPaymentMethod = 1024 | ||||
| @@ -4613,6 +4635,7 @@ var EPaymentMethod_name = map[EPaymentMethod]string{ | ||||
| 	129:  "EPaymentMethod_Valve", | ||||
| 	130:  "EPaymentMethod_SteamPressMaster", | ||||
| 	131:  "EPaymentMethod_StorePromotion", | ||||
| 	134:  "EPaymentMethod_MasterSubscription", | ||||
| 	256:  "EPaymentMethod_OEMTicket", | ||||
| 	512:  "EPaymentMethod_Split", | ||||
| 	1024: "EPaymentMethod_Complimentary", | ||||
| @@ -4706,6 +4729,22 @@ const ( | ||||
| 	EPurchaseResultDetail_PurchaseCannotBeReplayed                EPurchaseResultDetail = 65 | ||||
| 	EPurchaseResultDetail_DelayedCompletion                       EPurchaseResultDetail = 66 | ||||
| 	EPurchaseResultDetail_BundleTypeCannotBeGifted                EPurchaseResultDetail = 67 | ||||
| 	EPurchaseResultDetail_BlockedByUSGov                          EPurchaseResultDetail = 68 | ||||
| 	EPurchaseResultDetail_ItemsReservedForCommercialUse           EPurchaseResultDetail = 69 | ||||
| 	EPurchaseResultDetail_GiftAlreadyOwned                        EPurchaseResultDetail = 70 | ||||
| 	EPurchaseResultDetail_GiftInvalidForRecipientRegion           EPurchaseResultDetail = 71 | ||||
| 	EPurchaseResultDetail_GiftPricingImbalance                    EPurchaseResultDetail = 72 | ||||
| 	EPurchaseResultDetail_GiftRecipientNotSpecified               EPurchaseResultDetail = 73 | ||||
| 	EPurchaseResultDetail_ItemsNotAllowedForCommercialUse         EPurchaseResultDetail = 74 | ||||
| 	EPurchaseResultDetail_BusinessStoreCountryCodeMismatch        EPurchaseResultDetail = 75 | ||||
| 	EPurchaseResultDetail_UserAssociatedWithManyCafes             EPurchaseResultDetail = 76 | ||||
| 	EPurchaseResultDetail_UserNotAssociatedWithCafe               EPurchaseResultDetail = 77 | ||||
| 	EPurchaseResultDetail_AddressInvalid                          EPurchaseResultDetail = 78 | ||||
| 	EPurchaseResultDetail_CreditCardNumberInvalid                 EPurchaseResultDetail = 79 | ||||
| 	EPurchaseResultDetail_CannotShipToMilitaryPostOffice          EPurchaseResultDetail = 80 | ||||
| 	EPurchaseResultDetail_BillingNameInvalidResemblesCreditCard   EPurchaseResultDetail = 81 | ||||
| 	EPurchaseResultDetail_PaymentMethodTemporarilyUnavailable     EPurchaseResultDetail = 82 | ||||
| 	EPurchaseResultDetail_PaymentMethodNotSupportedForProduct     EPurchaseResultDetail = 83 | ||||
| ) | ||||
|  | ||||
| var EPurchaseResultDetail_name = map[EPurchaseResultDetail]string{ | ||||
| @@ -4777,6 +4816,22 @@ var EPurchaseResultDetail_name = map[EPurchaseResultDetail]string{ | ||||
| 	65: "EPurchaseResultDetail_PurchaseCannotBeReplayed", | ||||
| 	66: "EPurchaseResultDetail_DelayedCompletion", | ||||
| 	67: "EPurchaseResultDetail_BundleTypeCannotBeGifted", | ||||
| 	68: "EPurchaseResultDetail_BlockedByUSGov", | ||||
| 	69: "EPurchaseResultDetail_ItemsReservedForCommercialUse", | ||||
| 	70: "EPurchaseResultDetail_GiftAlreadyOwned", | ||||
| 	71: "EPurchaseResultDetail_GiftInvalidForRecipientRegion", | ||||
| 	72: "EPurchaseResultDetail_GiftPricingImbalance", | ||||
| 	73: "EPurchaseResultDetail_GiftRecipientNotSpecified", | ||||
| 	74: "EPurchaseResultDetail_ItemsNotAllowedForCommercialUse", | ||||
| 	75: "EPurchaseResultDetail_BusinessStoreCountryCodeMismatch", | ||||
| 	76: "EPurchaseResultDetail_UserAssociatedWithManyCafes", | ||||
| 	77: "EPurchaseResultDetail_UserNotAssociatedWithCafe", | ||||
| 	78: "EPurchaseResultDetail_AddressInvalid", | ||||
| 	79: "EPurchaseResultDetail_CreditCardNumberInvalid", | ||||
| 	80: "EPurchaseResultDetail_CannotShipToMilitaryPostOffice", | ||||
| 	81: "EPurchaseResultDetail_BillingNameInvalidResemblesCreditCard", | ||||
| 	82: "EPurchaseResultDetail_PaymentMethodTemporarilyUnavailable", | ||||
| 	83: "EPurchaseResultDetail_PaymentMethodNotSupportedForProduct", | ||||
| } | ||||
|  | ||||
| func (e EPurchaseResultDetail) String() string { | ||||
| @@ -5275,7 +5330,8 @@ const ( | ||||
| 	EAppInfoSection_Store             EAppInfoSection = 16 | ||||
| 	EAppInfoSection_Localization      EAppInfoSection = 17 | ||||
| 	EAppInfoSection_Broadcastgamedata EAppInfoSection = 18 | ||||
| 	EAppInfoSection_Max               EAppInfoSection = 19 | ||||
| 	EAppInfoSection_Computed          EAppInfoSection = 19 | ||||
| 	EAppInfoSection_Albummetadata     EAppInfoSection = 20 | ||||
| ) | ||||
|  | ||||
| var EAppInfoSection_name = map[EAppInfoSection]string{ | ||||
| @@ -5298,7 +5354,8 @@ var EAppInfoSection_name = map[EAppInfoSection]string{ | ||||
| 	16: "EAppInfoSection_Store", | ||||
| 	17: "EAppInfoSection_Localization", | ||||
| 	18: "EAppInfoSection_Broadcastgamedata", | ||||
| 	19: "EAppInfoSection_Max", | ||||
| 	19: "EAppInfoSection_Computed", | ||||
| 	20: "EAppInfoSection_Albummetadata", | ||||
| } | ||||
|  | ||||
| func (e EAppInfoSection) String() string { | ||||
| @@ -5330,7 +5387,7 @@ const ( | ||||
| 	EContentDownloadSourceType_SLS        EContentDownloadSourceType = 6 | ||||
| 	EContentDownloadSourceType_SteamCache EContentDownloadSourceType = 7 | ||||
| 	EContentDownloadSourceType_OpenCache  EContentDownloadSourceType = 8 | ||||
| 	EContentDownloadSourceType_Max        EContentDownloadSourceType = 9 | ||||
| 	EContentDownloadSourceType_LANCache   EContentDownloadSourceType = 9 | ||||
| ) | ||||
|  | ||||
| var EContentDownloadSourceType_name = map[EContentDownloadSourceType]string{ | ||||
| @@ -5343,7 +5400,7 @@ var EContentDownloadSourceType_name = map[EContentDownloadSourceType]string{ | ||||
| 	6: "EContentDownloadSourceType_SLS", | ||||
| 	7: "EContentDownloadSourceType_SteamCache", | ||||
| 	8: "EContentDownloadSourceType_OpenCache", | ||||
| 	9: "EContentDownloadSourceType_Max", | ||||
| 	9: "EContentDownloadSourceType_LANCache", | ||||
| } | ||||
|  | ||||
| func (e EContentDownloadSourceType) String() string { | ||||
| @@ -5373,7 +5430,6 @@ const ( | ||||
| 	EPlatformType_OSX     EPlatformType = 4 | ||||
| 	EPlatformType_PS3     EPlatformType = 5 | ||||
| 	EPlatformType_Linux32 EPlatformType = 6 | ||||
| 	EPlatformType_Max     EPlatformType = 7 | ||||
| ) | ||||
|  | ||||
| var EPlatformType_name = map[EPlatformType]string{ | ||||
| @@ -5384,7 +5440,6 @@ var EPlatformType_name = map[EPlatformType]string{ | ||||
| 	4: "EPlatformType_OSX", | ||||
| 	5: "EPlatformType_PS3", | ||||
| 	6: "EPlatformType_Linux32", | ||||
| 	7: "EPlatformType_Max", | ||||
| } | ||||
|  | ||||
| func (e EPlatformType) String() string { | ||||
| @@ -5462,6 +5517,7 @@ const ( | ||||
| 	EOSType_MacOS1012      EOSType = -85 | ||||
| 	EOSType_Macos1013      EOSType = -84 | ||||
| 	EOSType_Macos1014      EOSType = -83 | ||||
| 	EOSType_Macos1015      EOSType = -82 | ||||
| 	EOSType_MacOSMax       EOSType = -1 | ||||
| 	EOSType_LinuxUnknown   EOSType = -203 | ||||
| 	EOSType_Linux22        EOSType = -202 | ||||
| @@ -5501,7 +5557,6 @@ const ( | ||||
| 	EOSType_Windows10      EOSType = 16 | ||||
| 	EOSType_Win2016        EOSType = 17 | ||||
| 	EOSType_WinMAX         EOSType = 18 | ||||
| 	EOSType_Max            EOSType = 26 | ||||
| ) | ||||
|  | ||||
| var EOSType_name = map[EOSType]string{ | ||||
| @@ -5560,6 +5615,7 @@ var EOSType_name = map[EOSType]string{ | ||||
| 	-85:  "EOSType_MacOS1012", | ||||
| 	-84:  "EOSType_Macos1013", | ||||
| 	-83:  "EOSType_Macos1014", | ||||
| 	-82:  "EOSType_Macos1015", | ||||
| 	-203: "EOSType_LinuxUnknown", | ||||
| 	-202: "EOSType_Linux22", | ||||
| 	-201: "EOSType_Linux24", | ||||
| @@ -5597,7 +5653,6 @@ var EOSType_name = map[EOSType]string{ | ||||
| 	16:   "EOSType_Win10", | ||||
| 	17:   "EOSType_Win2016", | ||||
| 	18:   "EOSType_WinMAX", | ||||
| 	26:   "EOSType_Max", | ||||
| } | ||||
|  | ||||
| func (e EOSType) String() string { | ||||
| @@ -5734,7 +5789,19 @@ const ( | ||||
| 	EServerType_TimeMachine         EServerType = 111 | ||||
| 	EServerType_VACDBMaster         EServerType = 112 | ||||
| 	EServerType_ContentServerConfig EServerType = 113 | ||||
| 	EServerType_Max                 EServerType = 114 | ||||
| 	EServerType_Minigame            EServerType = 114 | ||||
| 	EServerType_MLTrain             EServerType = 115 | ||||
| 	EServerType_VACTest             EServerType = 116 | ||||
| 	EServerType_TaxService          EServerType = 117 | ||||
| 	EServerType_MLInference         EServerType = 118 | ||||
| 	EServerType_UGSAggregate        EServerType = 119 | ||||
| 	EServerType_TURN                EServerType = 120 | ||||
| 	EServerType_RemoteClient        EServerType = 121 | ||||
| 	EServerType_BroadcastOrigin     EServerType = 122 | ||||
| 	EServerType_BroadcastChannel    EServerType = 123 | ||||
| 	EServerType_SteamAR             EServerType = 124 | ||||
| 	EServerType_China               EServerType = 125 | ||||
| 	EServerType_CrashDump           EServerType = 126 | ||||
| ) | ||||
|  | ||||
| var EServerType_name = map[EServerType]string{ | ||||
| @@ -5856,7 +5923,19 @@ var EServerType_name = map[EServerType]string{ | ||||
| 	111: "EServerType_TimeMachine", | ||||
| 	112: "EServerType_VACDBMaster", | ||||
| 	113: "EServerType_ContentServerConfig", | ||||
| 	114: "EServerType_Max", | ||||
| 	114: "EServerType_Minigame", | ||||
| 	115: "EServerType_MLTrain", | ||||
| 	116: "EServerType_VACTest", | ||||
| 	117: "EServerType_TaxService", | ||||
| 	118: "EServerType_MLInference", | ||||
| 	119: "EServerType_UGSAggregate", | ||||
| 	120: "EServerType_TURN", | ||||
| 	121: "EServerType_RemoteClient", | ||||
| 	122: "EServerType_BroadcastOrigin", | ||||
| 	123: "EServerType_BroadcastChannel", | ||||
| 	124: "EServerType_SteamAR", | ||||
| 	125: "EServerType_China", | ||||
| 	126: "EServerType_CrashDump", | ||||
| } | ||||
|  | ||||
| func (e EServerType) String() string { | ||||
| @@ -6102,7 +6181,6 @@ const ( | ||||
| 	ECurrencyCode_QAR     ECurrencyCode = 39 | ||||
| 	ECurrencyCode_CRC     ECurrencyCode = 40 | ||||
| 	ECurrencyCode_UYU     ECurrencyCode = 41 | ||||
| 	ECurrencyCode_Max     ECurrencyCode = 42 | ||||
| ) | ||||
|  | ||||
| var ECurrencyCode_name = map[ECurrencyCode]string{ | ||||
| @@ -6147,7 +6225,6 @@ var ECurrencyCode_name = map[ECurrencyCode]string{ | ||||
| 	39: "ECurrencyCode_QAR", | ||||
| 	40: "ECurrencyCode_CRC", | ||||
| 	41: "ECurrencyCode_UYU", | ||||
| 	42: "ECurrencyCode_Max", | ||||
| } | ||||
|  | ||||
| func (e ECurrencyCode) String() string { | ||||
| @@ -6302,7 +6379,6 @@ const ( | ||||
| 	EWorkshopFileType_SteamworksAccessInvite EWorkshopFileType = 13 | ||||
| 	EWorkshopFileType_SteamVideo             EWorkshopFileType = 14 | ||||
| 	EWorkshopFileType_GameManagedItem        EWorkshopFileType = 15 | ||||
| 	EWorkshopFileType_Max                    EWorkshopFileType = 16 | ||||
| ) | ||||
|  | ||||
| var EWorkshopFileType_name = map[EWorkshopFileType]string{ | ||||
| @@ -6322,7 +6398,6 @@ var EWorkshopFileType_name = map[EWorkshopFileType]string{ | ||||
| 	13: "EWorkshopFileType_SteamworksAccessInvite", | ||||
| 	14: "EWorkshopFileType_SteamVideo", | ||||
| 	15: "EWorkshopFileType_GameManagedItem", | ||||
| 	16: "EWorkshopFileType_Max", | ||||
| } | ||||
|  | ||||
| func (e EWorkshopFileType) String() string { | ||||
| @@ -6548,21 +6623,19 @@ const ( | ||||
| 	ESystemIMType_GiftRevoked              ESystemIMType = 7 | ||||
| 	ESystemIMType_SupportMessage           ESystemIMType = 8 | ||||
| 	ESystemIMType_SupportMessageClearAlert ESystemIMType = 9 | ||||
| 	ESystemIMType_Max                      ESystemIMType = 10 | ||||
| ) | ||||
|  | ||||
| var ESystemIMType_name = map[ESystemIMType]string{ | ||||
| 	0:  "ESystemIMType_RawText", | ||||
| 	1:  "ESystemIMType_InvalidCard", | ||||
| 	2:  "ESystemIMType_RecurringPurchaseFailed", | ||||
| 	3:  "ESystemIMType_CardWillExpire", | ||||
| 	4:  "ESystemIMType_SubscriptionExpired", | ||||
| 	5:  "ESystemIMType_GuestPassReceived", | ||||
| 	6:  "ESystemIMType_GuestPassGranted", | ||||
| 	7:  "ESystemIMType_GiftRevoked", | ||||
| 	8:  "ESystemIMType_SupportMessage", | ||||
| 	9:  "ESystemIMType_SupportMessageClearAlert", | ||||
| 	10: "ESystemIMType_Max", | ||||
| 	0: "ESystemIMType_RawText", | ||||
| 	1: "ESystemIMType_InvalidCard", | ||||
| 	2: "ESystemIMType_RecurringPurchaseFailed", | ||||
| 	3: "ESystemIMType_CardWillExpire", | ||||
| 	4: "ESystemIMType_SubscriptionExpired", | ||||
| 	5: "ESystemIMType_GuestPassReceived", | ||||
| 	6: "ESystemIMType_GuestPassGranted", | ||||
| 	7: "ESystemIMType_GiftRevoked", | ||||
| 	8: "ESystemIMType_SupportMessage", | ||||
| 	9: "ESystemIMType_SupportMessageClearAlert", | ||||
| } | ||||
|  | ||||
| func (e ESystemIMType) String() string { | ||||
| @@ -6618,15 +6691,15 @@ func (e EChatFlags) String() string { | ||||
| type ERemoteStoragePlatform int32 | ||||
|  | ||||
| const ( | ||||
| 	ERemoteStoragePlatform_None      ERemoteStoragePlatform = 0 | ||||
| 	ERemoteStoragePlatform_Windows   ERemoteStoragePlatform = 1 | ||||
| 	ERemoteStoragePlatform_OSX       ERemoteStoragePlatform = 2 | ||||
| 	ERemoteStoragePlatform_PS3       ERemoteStoragePlatform = 4 | ||||
| 	ERemoteStoragePlatform_Linux     ERemoteStoragePlatform = 8 | ||||
| 	ERemoteStoragePlatform_Reserved2 ERemoteStoragePlatform = 16 | ||||
| 	ERemoteStoragePlatform_Android   ERemoteStoragePlatform = 32 | ||||
| 	ERemoteStoragePlatform_IPhoneOS  ERemoteStoragePlatform = 64 | ||||
| 	ERemoteStoragePlatform_All       ERemoteStoragePlatform = -1 | ||||
| 	ERemoteStoragePlatform_None     ERemoteStoragePlatform = 0 | ||||
| 	ERemoteStoragePlatform_Windows  ERemoteStoragePlatform = 1 | ||||
| 	ERemoteStoragePlatform_OSX      ERemoteStoragePlatform = 2 | ||||
| 	ERemoteStoragePlatform_PS3      ERemoteStoragePlatform = 4 | ||||
| 	ERemoteStoragePlatform_Linux    ERemoteStoragePlatform = 8 | ||||
| 	ERemoteStoragePlatform_Switch   ERemoteStoragePlatform = 16 | ||||
| 	ERemoteStoragePlatform_Android  ERemoteStoragePlatform = 32 | ||||
| 	ERemoteStoragePlatform_IPhoneOS ERemoteStoragePlatform = 64 | ||||
| 	ERemoteStoragePlatform_All      ERemoteStoragePlatform = -1 | ||||
| ) | ||||
|  | ||||
| var ERemoteStoragePlatform_name = map[ERemoteStoragePlatform]string{ | ||||
| @@ -6635,7 +6708,7 @@ var ERemoteStoragePlatform_name = map[ERemoteStoragePlatform]string{ | ||||
| 	2:  "ERemoteStoragePlatform_OSX", | ||||
| 	4:  "ERemoteStoragePlatform_PS3", | ||||
| 	8:  "ERemoteStoragePlatform_Linux", | ||||
| 	16: "ERemoteStoragePlatform_Reserved2", | ||||
| 	16: "ERemoteStoragePlatform_Switch", | ||||
| 	32: "ERemoteStoragePlatform_Android", | ||||
| 	64: "ERemoteStoragePlatform_IPhoneOS", | ||||
| 	-1: "ERemoteStoragePlatform_All", | ||||
| @@ -6780,7 +6853,6 @@ const ( | ||||
| 	EClientStat_P2PGameConnections  EClientStat = 2 | ||||
| 	EClientStat_P2PVoiceConnections EClientStat = 3 | ||||
| 	EClientStat_BytesDownloaded     EClientStat = 4 | ||||
| 	EClientStat_Max                 EClientStat = 5 | ||||
| ) | ||||
|  | ||||
| var EClientStat_name = map[EClientStat]string{ | ||||
| @@ -6789,7 +6861,6 @@ var EClientStat_name = map[EClientStat]string{ | ||||
| 	2: "EClientStat_P2PGameConnections", | ||||
| 	3: "EClientStat_P2PVoiceConnections", | ||||
| 	4: "EClientStat_BytesDownloaded", | ||||
| 	5: "EClientStat_Max", | ||||
| } | ||||
|  | ||||
| func (e EClientStat) String() string { | ||||
| @@ -7106,6 +7177,7 @@ type EPublishedFileInappropriateResult int32 | ||||
| const ( | ||||
| 	EPublishedFileInappropriateResult_NotScanned   EPublishedFileInappropriateResult = 0 | ||||
| 	EPublishedFileInappropriateResult_VeryUnlikely EPublishedFileInappropriateResult = 1 | ||||
| 	EPublishedFileInappropriateResult_Unlikely     EPublishedFileInappropriateResult = 30 | ||||
| 	EPublishedFileInappropriateResult_Possible     EPublishedFileInappropriateResult = 50 | ||||
| 	EPublishedFileInappropriateResult_Likely       EPublishedFileInappropriateResult = 75 | ||||
| 	EPublishedFileInappropriateResult_VeryLikely   EPublishedFileInappropriateResult = 100 | ||||
| @@ -7114,6 +7186,7 @@ const ( | ||||
| var EPublishedFileInappropriateResult_name = map[EPublishedFileInappropriateResult]string{ | ||||
| 	0:   "EPublishedFileInappropriateResult_NotScanned", | ||||
| 	1:   "EPublishedFileInappropriateResult_VeryUnlikely", | ||||
| 	30:  "EPublishedFileInappropriateResult_Unlikely", | ||||
| 	50:  "EPublishedFileInappropriateResult_Possible", | ||||
| 	75:  "EPublishedFileInappropriateResult_Likely", | ||||
| 	100: "EPublishedFileInappropriateResult_VeryLikely", | ||||
| @@ -7723,6 +7796,282 @@ func (e ETradeOfferConfirmationMethod) String() string { | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ELobbyType int32 | ||||
|  | ||||
| const ( | ||||
| 	ELobbyType_Private       ELobbyType = 0 | ||||
| 	ELobbyType_FriendsOnly   ELobbyType = 1 | ||||
| 	ELobbyType_Public        ELobbyType = 2 | ||||
| 	ELobbyType_Invisible     ELobbyType = 3 | ||||
| 	ELobbyType_PrivateUnique ELobbyType = 4 | ||||
| ) | ||||
|  | ||||
| var ELobbyType_name = map[ELobbyType]string{ | ||||
| 	0: "ELobbyType_Private", | ||||
| 	1: "ELobbyType_FriendsOnly", | ||||
| 	2: "ELobbyType_Public", | ||||
| 	3: "ELobbyType_Invisible", | ||||
| 	4: "ELobbyType_PrivateUnique", | ||||
| } | ||||
|  | ||||
| func (e ELobbyType) String() string { | ||||
| 	if s, ok := ELobbyType_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ELobbyType_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ELobbyFilterType int32 | ||||
|  | ||||
| const ( | ||||
| 	ELobbyFilterType_String         ELobbyFilterType = 0 | ||||
| 	ELobbyFilterType_Numerical      ELobbyFilterType = 1 | ||||
| 	ELobbyFilterType_SlotsAvailable ELobbyFilterType = 2 | ||||
| 	ELobbyFilterType_NearValue      ELobbyFilterType = 3 | ||||
| 	ELobbyFilterType_Distance       ELobbyFilterType = 4 | ||||
| ) | ||||
|  | ||||
| var ELobbyFilterType_name = map[ELobbyFilterType]string{ | ||||
| 	0: "ELobbyFilterType_String", | ||||
| 	1: "ELobbyFilterType_Numerical", | ||||
| 	2: "ELobbyFilterType_SlotsAvailable", | ||||
| 	3: "ELobbyFilterType_NearValue", | ||||
| 	4: "ELobbyFilterType_Distance", | ||||
| } | ||||
|  | ||||
| func (e ELobbyFilterType) String() string { | ||||
| 	if s, ok := ELobbyFilterType_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ELobbyFilterType_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ELobbyComparison int32 | ||||
|  | ||||
| const ( | ||||
| 	ELobbyComparison_EqualToOrLessThan    ELobbyComparison = -2 | ||||
| 	ELobbyComparison_LessThan             ELobbyComparison = -1 | ||||
| 	ELobbyComparison_Equal                ELobbyComparison = 0 | ||||
| 	ELobbyComparison_GreaterThan          ELobbyComparison = 1 | ||||
| 	ELobbyComparison_EqualToOrGreaterThan ELobbyComparison = 2 | ||||
| 	ELobbyComparison_NotEqual             ELobbyComparison = 3 | ||||
| ) | ||||
|  | ||||
| var ELobbyComparison_name = map[ELobbyComparison]string{ | ||||
| 	-2: "ELobbyComparison_EqualToOrLessThan", | ||||
| 	-1: "ELobbyComparison_LessThan", | ||||
| 	0:  "ELobbyComparison_Equal", | ||||
| 	1:  "ELobbyComparison_GreaterThan", | ||||
| 	2:  "ELobbyComparison_EqualToOrGreaterThan", | ||||
| 	3:  "ELobbyComparison_NotEqual", | ||||
| } | ||||
|  | ||||
| func (e ELobbyComparison) String() string { | ||||
| 	if s, ok := ELobbyComparison_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ELobbyComparison_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ELobbyDistanceFilter int32 | ||||
|  | ||||
| const ( | ||||
| 	ELobbyDistanceFilter_Close     ELobbyDistanceFilter = 0 | ||||
| 	ELobbyDistanceFilter_Default   ELobbyDistanceFilter = 1 | ||||
| 	ELobbyDistanceFilter_Far       ELobbyDistanceFilter = 2 | ||||
| 	ELobbyDistanceFilter_Worldwide ELobbyDistanceFilter = 3 | ||||
| ) | ||||
|  | ||||
| var ELobbyDistanceFilter_name = map[ELobbyDistanceFilter]string{ | ||||
| 	0: "ELobbyDistanceFilter_Close", | ||||
| 	1: "ELobbyDistanceFilter_Default", | ||||
| 	2: "ELobbyDistanceFilter_Far", | ||||
| 	3: "ELobbyDistanceFilter_Worldwide", | ||||
| } | ||||
|  | ||||
| func (e ELobbyDistanceFilter) String() string { | ||||
| 	if s, ok := ELobbyDistanceFilter_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ELobbyDistanceFilter_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ESteamIPv6ConnectivityProtocol int32 | ||||
|  | ||||
| const ( | ||||
| 	ESteamIPv6ConnectivityProtocol_Invalid ESteamIPv6ConnectivityProtocol = 0 | ||||
| 	ESteamIPv6ConnectivityProtocol_Http    ESteamIPv6ConnectivityProtocol = 1 | ||||
| 	ESteamIPv6ConnectivityProtocol_Udp     ESteamIPv6ConnectivityProtocol = 2 | ||||
| ) | ||||
|  | ||||
| var ESteamIPv6ConnectivityProtocol_name = map[ESteamIPv6ConnectivityProtocol]string{ | ||||
| 	0: "ESteamIPv6ConnectivityProtocol_Invalid", | ||||
| 	1: "ESteamIPv6ConnectivityProtocol_Http", | ||||
| 	2: "ESteamIPv6ConnectivityProtocol_Udp", | ||||
| } | ||||
|  | ||||
| func (e ESteamIPv6ConnectivityProtocol) String() string { | ||||
| 	if s, ok := ESteamIPv6ConnectivityProtocol_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ESteamIPv6ConnectivityProtocol_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ESteamIPv6ConnectivityState int32 | ||||
|  | ||||
| const ( | ||||
| 	ESteamIPv6ConnectivityState_Unknown ESteamIPv6ConnectivityState = 0 | ||||
| 	ESteamIPv6ConnectivityState_Good    ESteamIPv6ConnectivityState = 1 | ||||
| 	ESteamIPv6ConnectivityState_Bad     ESteamIPv6ConnectivityState = 2 | ||||
| ) | ||||
|  | ||||
| var ESteamIPv6ConnectivityState_name = map[ESteamIPv6ConnectivityState]string{ | ||||
| 	0: "ESteamIPv6ConnectivityState_Unknown", | ||||
| 	1: "ESteamIPv6ConnectivityState_Good", | ||||
| 	2: "ESteamIPv6ConnectivityState_Bad", | ||||
| } | ||||
|  | ||||
| func (e ESteamIPv6ConnectivityState) String() string { | ||||
| 	if s, ok := ESteamIPv6ConnectivityState_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ESteamIPv6ConnectivityState_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ESteamRealm int32 | ||||
|  | ||||
| const ( | ||||
| 	ESteamRealm_Unknown     ESteamRealm = 0 | ||||
| 	ESteamRealm_SteamGlobal ESteamRealm = 1 | ||||
| 	ESteamRealm_SteamChina  ESteamRealm = 2 | ||||
| ) | ||||
|  | ||||
| var ESteamRealm_name = map[ESteamRealm]string{ | ||||
| 	0: "ESteamRealm_Unknown", | ||||
| 	1: "ESteamRealm_SteamGlobal", | ||||
| 	2: "ESteamRealm_SteamChina", | ||||
| } | ||||
|  | ||||
| func (e ESteamRealm) String() string { | ||||
| 	if s, ok := ESteamRealm_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ESteamRealm_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type ELauncherType int32 | ||||
|  | ||||
| const ( | ||||
| 	ELauncherType_Default      ELauncherType = 0 | ||||
| 	ELauncherType_PerfectWorld ELauncherType = 1 | ||||
| 	ELauncherType_Nexon        ELauncherType = 2 | ||||
| 	ELauncherType_CmdLine      ELauncherType = 3 | ||||
| 	ELauncherType_CSGO         ELauncherType = 4 | ||||
| 	ELauncherType_ClientUI     ELauncherType = 5 | ||||
| 	ELauncherType_Headless     ELauncherType = 6 | ||||
| 	ELauncherType_SteamChina   ELauncherType = 7 | ||||
| 	ELauncherType_SingleApp    ELauncherType = 8 | ||||
| ) | ||||
|  | ||||
| var ELauncherType_name = map[ELauncherType]string{ | ||||
| 	0: "ELauncherType_Default", | ||||
| 	1: "ELauncherType_PerfectWorld", | ||||
| 	2: "ELauncherType_Nexon", | ||||
| 	3: "ELauncherType_CmdLine", | ||||
| 	4: "ELauncherType_CSGO", | ||||
| 	5: "ELauncherType_ClientUI", | ||||
| 	6: "ELauncherType_Headless", | ||||
| 	7: "ELauncherType_SteamChina", | ||||
| 	8: "ELauncherType_SingleApp", | ||||
| } | ||||
|  | ||||
| func (e ELauncherType) String() string { | ||||
| 	if s, ok := ELauncherType_name[e]; ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	var flags []string | ||||
| 	for k, v := range ELauncherType_name { | ||||
| 		if e&k != 0 { | ||||
| 			flags = append(flags, v) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(flags) == 0 { | ||||
| 		return fmt.Sprintf("%d", e) | ||||
| 	} | ||||
| 	sort.Strings(flags) | ||||
| 	return strings.Join(flags, " | ") | ||||
| } | ||||
|  | ||||
| type EUdpPacketType uint8 | ||||
|  | ||||
| const ( | ||||
|   | ||||
							
								
								
									
										27
									
								
								vendor/github.com/Philipp15b/go-steam/social.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/Philipp15b/go-steam/social.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -84,14 +84,14 @@ func (s *Social) SetPersonaState(state EPersonaState) { | ||||
|  | ||||
| // Sends a chat message to ether a room or friend | ||||
| func (s *Social) SendMessage(to SteamId, entryType EChatEntryType, message string) { | ||||
| 	//Friend | ||||
| 	// Friend | ||||
| 	if to.GetAccountType() == int32(EAccountType_Individual) || to.GetAccountType() == int32(EAccountType_ConsoleUser) { | ||||
| 		s.client.Write(NewClientMsgProtobuf(EMsg_ClientFriendMsg, &CMsgClientFriendMsg{ | ||||
| 			Steamid:       proto.Uint64(to.ToUint64()), | ||||
| 			ChatEntryType: proto.Int32(int32(entryType)), | ||||
| 			Message:       []byte(message), | ||||
| 		})) | ||||
| 		//Chat room | ||||
| 		// Chat room | ||||
| 	} else if to.GetAccountType() == int32(EAccountType_Clan) || to.GetAccountType() == int32(EAccountType_Chat) { | ||||
| 		chatId := to.ClanToChat() | ||||
| 		s.client.Write(NewClientMsg(&MsgClientChatMsg{ | ||||
| @@ -119,9 +119,9 @@ func (s *Social) RemoveFriend(id SteamId) { | ||||
|  | ||||
| // Ignores or unignores a friend on Steam | ||||
| func (s *Social) IgnoreFriend(id SteamId, setIgnore bool) { | ||||
| 	ignore := uint8(1) //True | ||||
| 	ignore := uint8(1) // True | ||||
| 	if !setIgnore { | ||||
| 		ignore = uint8(0) //False | ||||
| 		ignore = uint8(0) // False | ||||
| 	} | ||||
| 	s.client.Write(NewClientMsg(&MsgClientSetIgnoreFriend{ | ||||
| 		MySteamId:     s.client.SteamId(), | ||||
| @@ -244,7 +244,7 @@ func (s *Social) HandlePacket(packet *Packet) { | ||||
| } | ||||
|  | ||||
| func (s *Social) handleAccountInfo(packet *Packet) { | ||||
| 	//Just fire the personainfo, Auth handles the callback | ||||
| 	// Just fire the personainfo, Auth handles the callback | ||||
| 	flags := EClientPersonaStateFlag_PlayerName | EClientPersonaStateFlag_Presence | EClientPersonaStateFlag_SourceID | ||||
| 	s.RequestFriendInfo(s.client.SteamId(), EClientPersonaStateFlag(flags)) | ||||
| } | ||||
| @@ -302,7 +302,7 @@ func (s *Social) handlePersonaState(packet *Packet) { | ||||
| 	flags := EClientPersonaStateFlag(list.GetStatusFlags()) | ||||
| 	for _, friend := range list.GetFriends() { | ||||
| 		id := SteamId(friend.GetFriendid()) | ||||
| 		if id == s.client.SteamId() { //this is our client id | ||||
| 		if id == s.client.SteamId() { // this is our client id | ||||
| 			s.mutex.Lock() | ||||
| 			if friend.GetPlayerName() != "" { | ||||
| 				s.name = friend.GetPlayerName() | ||||
| @@ -364,7 +364,6 @@ func (s *Social) handlePersonaState(packet *Packet) { | ||||
| 			ClanRank:               friend.GetClanRank(), | ||||
| 			ClanTag:                friend.GetClanTag(), | ||||
| 			OnlineSessionInstances: friend.GetOnlineSessionInstances(), | ||||
| 			PublishedSessionId:     friend.GetPublishedInstanceId(), | ||||
| 			PersonaSetByUser:       friend.GetPersonaSetByUser(), | ||||
| 		}) | ||||
| 	} | ||||
| @@ -407,7 +406,7 @@ func (s *Social) handleClanState(packet *Packet) { | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	//Add stuff to group | ||||
| 	// Add stuff to group | ||||
| 	clanid := SteamId(body.GetSteamidClan()) | ||||
| 	if body.NameInfo != nil { | ||||
| 		info := body.NameInfo | ||||
| @@ -473,14 +472,14 @@ func (s *Social) handleChatEnter(packet *Packet) { | ||||
| 	payload := packet.ReadClientMsg(body).Payload | ||||
| 	reader := bytes.NewBuffer(payload) | ||||
| 	name, _ := ReadString(reader) | ||||
| 	ReadByte(reader) //0 | ||||
| 	ReadByte(reader) // 0 | ||||
| 	count := body.NumMembers | ||||
| 	chatId := SteamId(body.SteamIdChat) | ||||
| 	clanId := SteamId(body.SteamIdClan) | ||||
| 	s.Chats.Add(socialcache.Chat{SteamId: chatId, GroupId: clanId}) | ||||
| 	for i := 0; i < int(count); i++ { | ||||
| 		id, chatPerm, clanPerm := readChatMember(reader) | ||||
| 		ReadBytes(reader, 6) //No idea what this is | ||||
| 		ReadBytes(reader, 6) // No idea what this is | ||||
| 		s.Chats.AddChatMember(chatId, socialcache.ChatMember{ | ||||
| 			SteamId:         SteamId(id), | ||||
| 			ChatPermissions: chatPerm, | ||||
| @@ -508,7 +507,7 @@ func (s *Social) handleChatMemberInfo(packet *Packet) { | ||||
| 		actedOn, _ := ReadUint64(reader) | ||||
| 		state, _ := ReadInt32(reader) | ||||
| 		actedBy, _ := ReadUint64(reader) | ||||
| 		ReadByte(reader) //0 | ||||
| 		ReadByte(reader) // 0 | ||||
| 		stateChange := EChatMemberStateChange(state) | ||||
| 		if stateChange == EChatMemberStateChange_Entered { | ||||
| 			_, chatPerm, clanPerm := readChatMember(reader) | ||||
| @@ -537,13 +536,13 @@ func (s *Social) handleChatMemberInfo(packet *Packet) { | ||||
| func readChatMember(r io.Reader) (SteamId, EChatPermission, EClanPermission) { | ||||
| 	ReadString(r) // MessageObject | ||||
| 	ReadByte(r)   // 7 | ||||
| 	ReadString(r) //steamid | ||||
| 	ReadString(r) // steamid | ||||
| 	id, _ := ReadUint64(r) | ||||
| 	ReadByte(r)   // 2 | ||||
| 	ReadString(r) //Permissions | ||||
| 	ReadString(r) // Permissions | ||||
| 	chat, _ := ReadInt32(r) | ||||
| 	ReadByte(r)   // 2 | ||||
| 	ReadString(r) //Details | ||||
| 	ReadString(r) // Details | ||||
| 	clan, _ := ReadInt32(r) | ||||
| 	return SteamId(id), EChatPermission(chat), EClanPermission(clan) | ||||
| } | ||||
|   | ||||
							
								
								
									
										1
									
								
								vendor/github.com/Philipp15b/go-steam/social_events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/Philipp15b/go-steam/social_events.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -48,7 +48,6 @@ type PersonaStateEvent struct { | ||||
| 	ClanRank               uint32 | ||||
| 	ClanTag                string | ||||
| 	OnlineSessionInstances uint32 | ||||
| 	PublishedSessionId     uint32 | ||||
| 	PersonaSetByUser       bool | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										8
									
								
								vendor/github.com/Rhymen/go-whatsapp/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/Rhymen/go-whatsapp/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -70,6 +70,14 @@ func (myHandler) HandleContactMessage(message whatsapp.ContactMessage) { | ||||
| 	fmt.Println(message) | ||||
| } | ||||
|  | ||||
| func (myHandler) HandleBatteryMessage(msg whatsapp.BatteryMessage) { | ||||
| 	fmt.Println(message) | ||||
| } | ||||
|  | ||||
| func (myHandler) HandleNewContact(contact whatsapp.Contact) { | ||||
| 	fmt.Println(contact) | ||||
| } | ||||
|  | ||||
| wac.AddHandler(myHandler{}) | ||||
| ``` | ||||
| The message handlers are all optional, you don't need to implement anything but the error handler to implement the interface. The ImageMessage, VideoMessage, AudioMessage and DocumentMessage provide a Download function to get the media data. | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -24,7 +24,10 @@ var SingleByteTokens = [...]string{"", "", "", "200", "400", "404", "500", "501" | ||||
| 	"invite", "gif", "vcard", "frequent", "privacy", "blacklist", "whitelist", | ||||
| 	"verify", "location", "document", "elapsed", "revoke_invite", "expiration", | ||||
| 	"unsubscribe", "disable", "vname", "old_jid", "new_jid", "announcement", | ||||
| 	"locked", "prop", "label", "color", "call", "offer", "call-id"} | ||||
| 	"locked", "prop", "label", "color", "call", "offer", "call-id", | ||||
| 	"quick_reply", "sticker", "pay_t", "accept", "reject", "sticker_pack", | ||||
| 	"invalid", "canceled", "missed", "connected", "result", "audio", | ||||
| 	"video", "recent"} | ||||
|  | ||||
| var doubleByteTokens = [...]string{} | ||||
|  | ||||
|   | ||||
							
								
								
									
										88
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/Rhymen/go-whatsapp/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -88,6 +88,8 @@ type Conn struct { | ||||
| 	Store          *Store | ||||
| 	ServerLastSeen time.Time | ||||
|  | ||||
| 	timeTag string // last 3 digits obtained after a successful login takeover | ||||
|  | ||||
| 	longClientName  string | ||||
| 	shortClientName string | ||||
| 	clientVersion   string | ||||
| @@ -114,31 +116,59 @@ Creates a new connection with a given timeout. The websocket connection to the W | ||||
| The goroutine for handling incoming messages is started | ||||
| */ | ||||
| func NewConn(timeout time.Duration) (*Conn, error) { | ||||
| 	wac := &Conn{ | ||||
| 		handler:    make([]Handler, 0), | ||||
| 		msgCount:   0, | ||||
| 		msgTimeout: timeout, | ||||
| 		Store:      newStore(), | ||||
|  | ||||
| 		longClientName:  "github.com/rhymen/go-whatsapp", | ||||
| 		shortClientName: "go-whatsapp", | ||||
| 		clientVersion:   "0.1.0", | ||||
| 	} | ||||
| 	return wac, wac.connect() | ||||
| 	return NewConnWithOptions(&Options{ | ||||
| 		Timeout: timeout, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // NewConnWithProxy Create a new connect with a given timeout and a http proxy. | ||||
| func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL, error)) (*Conn, error) { | ||||
| 	return NewConnWithOptions(&Options{ | ||||
| 		Timeout: timeout, | ||||
| 		Proxy: proxy, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // NewConnWithOptions Create a new connect with a given options. | ||||
| type Options struct { | ||||
| 	Proxy            func(*http.Request) (*url.URL, error) | ||||
| 	Timeout          time.Duration | ||||
| 	Handler          []Handler | ||||
| 	ShortClientName  string | ||||
| 	LongClientName   string | ||||
| 	ClientVersion    string | ||||
| 	Store            *Store | ||||
| } | ||||
| func NewConnWithOptions(opt *Options) (*Conn, error) { | ||||
| 	if opt == nil { | ||||
| 		return nil, ErrOptionsNotProvided | ||||
| 	} | ||||
| 	wac := &Conn{ | ||||
| 		handler:    make([]Handler, 0), | ||||
| 		msgCount:   0, | ||||
| 		msgTimeout: timeout, | ||||
| 		msgTimeout: opt.Timeout, | ||||
| 		Store:      newStore(), | ||||
|  | ||||
| 		longClientName:  "github.com/rhymen/go-whatsapp", | ||||
| 		longClientName:  "github.com/Rhymen/go-whatsapp", | ||||
| 		shortClientName: "go-whatsapp", | ||||
| 		clientVersion:   "0.1.0", | ||||
| 		Proxy:           proxy, | ||||
| 	} | ||||
| 	if opt.Handler != nil { | ||||
| 		wac.handler = opt.Handler | ||||
| 	} | ||||
| 	if opt.Store != nil { | ||||
| 		wac.Store = opt.Store | ||||
| 	} | ||||
| 	if opt.Proxy != nil { | ||||
| 		wac.Proxy = opt.Proxy | ||||
| 	} | ||||
| 	if len(opt.ShortClientName) != 0 { | ||||
| 		wac.shortClientName = opt.ShortClientName | ||||
| 	} | ||||
| 	if len(opt.LongClientName) != 0 { | ||||
| 		wac.longClientName = opt.LongClientName | ||||
| 	} | ||||
| 	if len(opt.ClientVersion) != 0 { | ||||
| 		wac.clientVersion = opt.ClientVersion | ||||
| 	} | ||||
| 	return wac, wac.connect() | ||||
| } | ||||
| @@ -156,8 +186,8 @@ func (wac *Conn) connect() (err error) { | ||||
| 	}() | ||||
|  | ||||
| 	dialer := &websocket.Dialer{ | ||||
| 		ReadBufferSize:   25 * 1024 * 1024, | ||||
| 		WriteBufferSize:  10 * 1024 * 1024, | ||||
| 		ReadBufferSize:   0, | ||||
| 		WriteBufferSize:  0, | ||||
| 		HandshakeTimeout: wac.msgTimeout, | ||||
| 		Proxy:            wac.Proxy, | ||||
| 	} | ||||
| @@ -246,3 +276,27 @@ func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // IsConnected returns whether the server connection is established or not | ||||
| func (wac *Conn) IsConnected() bool { | ||||
| 	return wac.connected | ||||
| } | ||||
|  | ||||
| // GetConnected returns whether the server connection is established or not | ||||
| // | ||||
| // Deprecated: function name is not go idiomatic, use IsConnected instead | ||||
| func (wac *Conn) GetConnected() bool { | ||||
| 	return wac.connected | ||||
| } | ||||
|  | ||||
| //IsLoggedIn returns whether the you are logged in or not | ||||
| func (wac *Conn) IsLoggedIn() bool { | ||||
| 	return wac.loggedIn | ||||
| } | ||||
|  | ||||
| // GetLoggedIn returns whether the you are logged in or not | ||||
| // | ||||
| // Deprecated: function name is not go idiomatic, use IsLoggedIn instead. | ||||
| func (wac *Conn) GetLoggedIn() bool { | ||||
| 	return wac.loggedIn | ||||
| } | ||||
|   | ||||
							
								
								
									
										47
									
								
								vendor/github.com/Rhymen/go-whatsapp/contact.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/Rhymen/go-whatsapp/contact.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,9 +2,11 @@ package whatsapp | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/Rhymen/go-whatsapp/binary" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/Rhymen/go-whatsapp/binary" | ||||
| ) | ||||
|  | ||||
| type Presence string | ||||
| @@ -241,3 +243,46 @@ func buildParticipantNodes(participants []string) []binary.Node { | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| func (wac *Conn) BlockContact(jid string) (<-chan string, error) { | ||||
| 	return wac.handleBlockContact("add", jid) | ||||
| } | ||||
|  | ||||
| func (wac *Conn) UnblockContact(jid string) (<-chan string, error) { | ||||
| 	return wac.handleBlockContact("remove", jid) | ||||
| } | ||||
|  | ||||
| func (wac *Conn) handleBlockContact(action, jid string) (<-chan string, error) { | ||||
| 	ts := time.Now().Unix() | ||||
| 	tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount) | ||||
|  | ||||
| 	netsplit := strings.Split(jid, "@") | ||||
| 	cusjid := netsplit[0] + "@c.us" | ||||
|  | ||||
| 	n := binary.Node{ | ||||
| 		Description: "action", | ||||
| 		Attributes: map[string]string{ | ||||
| 			"type":  "set", | ||||
| 			"epoch": strconv.Itoa(wac.msgCount), | ||||
| 		}, | ||||
| 		Content: []interface{}{ | ||||
| 			binary.Node{ | ||||
| 				Description: "block", | ||||
| 				Attributes: map[string]string{ | ||||
| 					"type": action, | ||||
| 				}, | ||||
| 				Content: []binary.Node{ | ||||
| 					{ | ||||
| 						Description: "user", | ||||
| 						Attributes: map[string]string{ | ||||
| 							"jid": cusjid, | ||||
| 						}, | ||||
| 						Content: nil, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return wac.writeBinary(n, contact, ignore, tag) | ||||
| } | ||||
|   | ||||
							
								
								
									
										30
									
								
								vendor/github.com/Rhymen/go-whatsapp/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/Rhymen/go-whatsapp/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,24 +2,26 @@ package whatsapp | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrAlreadyConnected           = errors.New("already connected") | ||||
| 	ErrAlreadyLoggedIn            = errors.New("already logged in") | ||||
| 	ErrInvalidSession             = errors.New("invalid session") | ||||
| 	ErrLoginInProgress            = errors.New("login or restore already running") | ||||
| 	ErrNotConnected               = errors.New("not connected") | ||||
| 	ErrInvalidWsData              = errors.New("received invalid data") | ||||
| 	ErrInvalidWsState             = errors.New("can't handle binary data when not logged in") | ||||
| 	ErrConnectionTimeout          = errors.New("connection timed out") | ||||
| 	ErrMissingMessageTag          = errors.New("no messageTag specified or to short") | ||||
| 	ErrInvalidHmac                = errors.New("invalid hmac") | ||||
| 	ErrInvalidServerResponse      = errors.New("invalid response received from server") | ||||
| 	ErrServerRespondedWith404     = errors.New("server responded with status 404") | ||||
| 	ErrMediaDownloadFailedWith404 = errors.New("download failed with status code 404") | ||||
| 	ErrMediaDownloadFailedWith410 = errors.New("download failed with status code 410") | ||||
| 	ErrAlreadyConnected          = errors.New("already connected") | ||||
| 	ErrAlreadyLoggedIn           = errors.New("already logged in") | ||||
| 	ErrInvalidSession            = errors.New("invalid session") | ||||
| 	ErrLoginInProgress           = errors.New("login or restore already running") | ||||
| 	ErrNotConnected              = errors.New("not connected") | ||||
| 	ErrInvalidWsData             = errors.New("received invalid data") | ||||
| 	ErrInvalidWsState            = errors.New("can't handle binary data when not logged in") | ||||
| 	ErrConnectionTimeout         = errors.New("connection timed out") | ||||
| 	ErrMissingMessageTag         = errors.New("no messageTag specified or to short") | ||||
| 	ErrInvalidHmac               = errors.New("invalid hmac") | ||||
| 	ErrInvalidServerResponse     = errors.New("invalid response received from server") | ||||
| 	ErrServerRespondedWith404    = errors.New("server responded with status 404") | ||||
| 	ErrInvalidWebsocket          = errors.New("invalid websocket") | ||||
| 	ErrMessageTypeNotImplemented = errors.New("message type not implemented") | ||||
| 	ErrOptionsNotProvided        = errors.New("new conn options not provided") | ||||
| ) | ||||
|  | ||||
| type ErrConnectionFailed struct { | ||||
|   | ||||
							
								
								
									
										1
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/Rhymen/go-whatsapp/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -32,5 +32,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6Zh | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= | ||||
| golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
|   | ||||
							
								
								
									
										46
									
								
								vendor/github.com/Rhymen/go-whatsapp/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/Rhymen/go-whatsapp/handler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -133,6 +133,22 @@ type ChatListHandler interface { | ||||
| 	HandleChatList(contacts []Chat) | ||||
| } | ||||
|  | ||||
| /** | ||||
| The BatteryMessageHandler interface needs to be implemented to receive percentage the device connected dispatched by the dispatcher. | ||||
| */ | ||||
| type BatteryMessageHandler interface { | ||||
| 	Handler | ||||
| 	HandleBatteryMessage(battery BatteryMessage) | ||||
| } | ||||
|  | ||||
| /** | ||||
| The NewContactHandler interface needs to be implemented to receive the contact's name for the first time. | ||||
| */ | ||||
| type NewContactHandler interface { | ||||
| 	Handler | ||||
| 	HandleNewContact(contact Contact) | ||||
| } | ||||
|  | ||||
| /* | ||||
| AddHandler adds an handler to the list of handler that receive dispatched messages. | ||||
| The provided handler must at least implement the Handler interface. Additionally implemented | ||||
| @@ -285,6 +301,28 @@ func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handle | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	 | ||||
| 	case BatteryMessage: | ||||
| 		for _, h := range handlers { | ||||
| 			if x, ok := h.(BatteryMessageHandler); ok { | ||||
| 				if wac.shouldCallSynchronously(h) { | ||||
| 					x.HandleBatteryMessage(m) | ||||
| 				} else { | ||||
| 					go x.HandleBatteryMessage(m) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	 | ||||
| 	case Contact: | ||||
| 		for _, h := range handlers { | ||||
| 			if x, ok := h.(NewContactHandler); ok { | ||||
| 				if wac.shouldCallSynchronously(h) { | ||||
| 					x.HandleNewContact(m) | ||||
| 				} else { | ||||
| 					go x.HandleNewContact(m) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	case *proto.WebMessageInfo: | ||||
| 		for _, h := range handlers { | ||||
| @@ -378,6 +416,14 @@ func (wac *Conn) dispatch(msg interface{}) { | ||||
| 						wac.handle(v) | ||||
| 						wac.handle(ParseProtoMessage(v)) | ||||
| 					} | ||||
|  | ||||
| 					if v, ok := con[a].(binary.Node); ok { | ||||
| 						wac.handle(ParseNodeMessage(v)) | ||||
| 					} | ||||
| 				} | ||||
| 			} else if con, ok := message.Content.([]binary.Node); ok { | ||||
| 				for a := range con { | ||||
| 					wac.handle(ParseNodeMessage(con[a])) | ||||
| 				} | ||||
| 			} | ||||
| 		} else if message.Description == "response" && message.Attributes["type"] == "contacts" { | ||||
|   | ||||
							
								
								
									
										46
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/Rhymen/go-whatsapp/media.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,6 +10,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"time" | ||||
| @@ -38,7 +39,7 @@ func Download(url string, mediaKey []byte, appInfo MediaType, fileLength int) ([ | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(data) != fileLength { | ||||
| 		return nil, fmt.Errorf("file length does not match") | ||||
| 		return nil, fmt.Errorf("file length does not match. Expected: %v, got: %v", fileLength, len(data)) | ||||
| 	} | ||||
| 	return data, nil | ||||
| } | ||||
| @@ -71,16 +72,10 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) { | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		if resp.StatusCode == 404 { | ||||
| 			return nil, nil, ErrMediaDownloadFailedWith404 | ||||
| 		} | ||||
| 		if resp.StatusCode == 410 { | ||||
| 			return nil, nil, ErrMediaDownloadFailedWith410 | ||||
| 		} | ||||
| 	defer resp.Body.Close() | ||||
| 	if resp.StatusCode != http.StatusOK { | ||||
| 		return nil, nil, fmt.Errorf("download failed with status code %d", resp.StatusCode) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	if resp.ContentLength <= 10 { | ||||
| 		return nil, nil, fmt.Errorf("file to short") | ||||
| 	} | ||||
| @@ -99,8 +94,11 @@ type MediaConn struct { | ||||
| 		Auth  string `json:"auth"` | ||||
| 		TTL   int    `json:"ttl"` | ||||
| 		Hosts []struct { | ||||
| 			Hostname string   `json:"hostname"` | ||||
| 			IPs      []string `json:"ips"` | ||||
| 			Hostname string `json:"hostname"` | ||||
| 			IPs      []struct { | ||||
| 				IP4 net.IP `json:"ip4"` | ||||
| 				IP6 net.IP `json:"ip6"` | ||||
| 			} `json:"ips"` | ||||
| 		} `json:"hosts"` | ||||
| 	} `json:"media_conn"` | ||||
| } | ||||
| @@ -122,18 +120,24 @@ func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) { | ||||
| 		return "", "", 0, fmt.Errorf("query media conn timed out") | ||||
| 	} | ||||
|  | ||||
| 	if resp.Status != 200 { | ||||
| 	if resp.Status != http.StatusOK { | ||||
| 		return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status) | ||||
| 	} | ||||
|  | ||||
| 	return resp.MediaConn.Hosts[0].Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil | ||||
| 	for _, h := range resp.MediaConn.Hosts { | ||||
| 		if h.Hostname != "" { | ||||
| 			return h.Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return "", "", 0, fmt.Errorf("query media conn responded with no host") | ||||
| } | ||||
|  | ||||
| var mediaTypeMap = map[MediaType]string{ | ||||
| 	MediaImage: "/mms/image", | ||||
| 	MediaVideo: "/mms/video", | ||||
| 	MediaImage:    "/mms/image", | ||||
| 	MediaVideo:    "/mms/video", | ||||
| 	MediaDocument: "/mms/document", | ||||
| 	MediaAudio: "/mms/audio", | ||||
| 	MediaAudio:    "/mms/audio", | ||||
| } | ||||
|  | ||||
| func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) { | ||||
| @@ -170,6 +174,10 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string | ||||
| 	fileEncSha256 = sha.Sum(nil) | ||||
|  | ||||
| 	hostname, auth, _, err := wac.queryMediaConn() | ||||
| 	if err != nil { | ||||
| 		return "", nil, nil, nil, 0, err | ||||
| 	} | ||||
|  | ||||
| 	token := base64.URLEncoding.EncodeToString(fileEncSha256) | ||||
| 	q := url.Values{ | ||||
| 		"auth":  []string{auth}, | ||||
| @@ -185,7 +193,7 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string | ||||
|  | ||||
| 	body := bytes.NewReader(append(enc, mac...)) | ||||
|  | ||||
| 	req, err := http.NewRequest("POST", uploadURL.String(), body) | ||||
| 	req, err := http.NewRequest(http.MethodPost, uploadURL.String(), body) | ||||
| 	if err != nil { | ||||
| 		return "", nil, nil, nil, 0, err | ||||
| 	} | ||||
| @@ -205,7 +213,9 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string | ||||
| 	} | ||||
|  | ||||
| 	var jsonRes map[string]string | ||||
| 	json.NewDecoder(res.Body).Decode(&jsonRes) | ||||
| 	if err := json.NewDecoder(res.Body).Decode(&jsonRes); err != nil { | ||||
| 		return "", nil, nil, nil, 0, err | ||||
| 	} | ||||
|  | ||||
| 	return jsonRes["url"], mediaKey, fileEncSha256, fileSha256, fileLength, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										145
									
								
								vendor/github.com/Rhymen/go-whatsapp/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										145
									
								
								vendor/github.com/Rhymen/go-whatsapp/message.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -81,7 +81,7 @@ func (wac *Conn) Send(msg interface{}) (string, error) { | ||||
| 			return "ERROR", fmt.Errorf("error decoding sending response: %v\n", err) | ||||
| 		} | ||||
| 		if int(resp["status"].(float64)) != 200 { | ||||
| 			return "ERROR", fmt.Errorf("message sending responded with %d", resp["status"]) | ||||
| 			return "ERROR", fmt.Errorf("message sending responded with %v", resp["status"]) | ||||
| 		} | ||||
| 		if int(resp["status"].(float64)) == 200 { | ||||
| 			return getMessageInfo(msgProto).Id, nil | ||||
| @@ -105,6 +105,105 @@ func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) { | ||||
| 	return wac.writeBinary(n, message, ignore, p.Key.GetId()) | ||||
| } | ||||
|  | ||||
| // RevokeMessage revokes a message (marks as "message removed") for everyone | ||||
| func (wac *Conn) RevokeMessage(remotejid, msgid string, fromme bool) (revokeid string, err error) { | ||||
| 	// create a revocation ID (required) | ||||
| 	rawrevocationID := make([]byte, 10) | ||||
| 	rand.Read(rawrevocationID) | ||||
| 	revocationID := strings.ToUpper(hex.EncodeToString(rawrevocationID)) | ||||
| 	// | ||||
| 	ts := uint64(time.Now().Unix()) | ||||
| 	status := proto.WebMessageInfo_PENDING | ||||
| 	mtype := proto.ProtocolMessage_REVOKE | ||||
|  | ||||
| 	revoker := &proto.WebMessageInfo{ | ||||
| 		Key: &proto.MessageKey{ | ||||
| 			FromMe:    &fromme, | ||||
| 			Id:        &revocationID, | ||||
| 			RemoteJid: &remotejid, | ||||
| 		}, | ||||
| 		MessageTimestamp: &ts, | ||||
| 		Message: &proto.Message{ | ||||
| 			ProtocolMessage: &proto.ProtocolMessage{ | ||||
| 				Type: &mtype, | ||||
| 				Key: &proto.MessageKey{ | ||||
| 					FromMe:    &fromme, | ||||
| 					Id:        &msgid, | ||||
| 					RemoteJid: &remotejid, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Status: &status, | ||||
| 	} | ||||
| 	if _, err := wac.Send(revoker); err != nil { | ||||
| 		return revocationID, err | ||||
| 	} | ||||
| 	return revocationID, nil | ||||
| } | ||||
|  | ||||
| // DeleteMessage deletes a single message for the user (removes the msgbox). To | ||||
| // delete the message for everyone, use RevokeMessage | ||||
| func (wac *Conn) DeleteMessage(remotejid, msgid string, fromMe bool) error { | ||||
| 	ch, err := wac.deleteChatProto(remotejid, msgid, fromMe) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("could not send proto: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	select { | ||||
| 	case response := <-ch: | ||||
| 		var resp map[string]interface{} | ||||
| 		if err = json.Unmarshal([]byte(response), &resp); err != nil { | ||||
| 			return fmt.Errorf("error decoding deletion response: %v", err) | ||||
| 		} | ||||
| 		if int(resp["status"].(float64)) != 200 { | ||||
| 			return fmt.Errorf("message deletion responded with %v", resp["status"]) | ||||
| 		} | ||||
| 		if int(resp["status"].(float64)) == 200 { | ||||
| 			return nil | ||||
| 		} | ||||
| 	case <-time.After(wac.msgTimeout): | ||||
| 		return fmt.Errorf("deleting message timed out") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (wac *Conn) deleteChatProto(remotejid, msgid string, fromMe bool) (<-chan string, error) { | ||||
| 	tag := fmt.Sprintf("%s.--%d", wac.timeTag, wac.msgCount) | ||||
|  | ||||
| 	owner := "true" | ||||
| 	if !fromMe { | ||||
| 		owner = "false" | ||||
| 	} | ||||
| 	n := binary.Node{ | ||||
| 		Description: "action", | ||||
| 		Attributes: map[string]string{ | ||||
| 			"epoch": strconv.Itoa(wac.msgCount), | ||||
| 			"type":  "set", | ||||
| 		}, | ||||
| 		Content: []interface{}{ | ||||
| 			binary.Node{ | ||||
| 				Description: "chat", | ||||
| 				Attributes: map[string]string{ | ||||
| 					"type":  "clear", | ||||
| 					"jid":   remotejid, | ||||
| 					"media": "true", | ||||
| 				}, | ||||
| 				Content: []binary.Node{ | ||||
| 					{ | ||||
| 						Description: "item", | ||||
| 						Attributes: map[string]string{ | ||||
| 							"owner": owner, | ||||
| 							"index": msgid, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return wac.writeBinary(n, chat, expires|skipOffline, tag) | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	rand.Seed(time.Now().UTC().UnixNano()) | ||||
| } | ||||
| @@ -139,7 +238,7 @@ func getMessageInfo(msg *proto.WebMessageInfo) MessageInfo { | ||||
| 	return MessageInfo{ | ||||
| 		Id:        msg.GetKey().GetId(), | ||||
| 		RemoteJid: msg.GetKey().GetRemoteJid(), | ||||
| 		SenderJid: msg.GetKey().GetParticipant(), | ||||
| 		SenderJid: msg.GetParticipant(), | ||||
| 		FromMe:    msg.GetKey().GetFromMe(), | ||||
| 		Timestamp: msg.GetMessageTimestamp(), | ||||
| 		Status:    MessageStatus(msg.GetStatus()), | ||||
| @@ -739,7 +838,49 @@ func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} { | ||||
|  | ||||
| 	default: | ||||
| 		//cannot match message | ||||
| 		return ErrMessageTypeNotImplemented | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
| BatteryMessage represents a battery level and charging state. | ||||
| */ | ||||
| type BatteryMessage struct { | ||||
| 	Plugged    bool | ||||
| 	Powersave  bool | ||||
| 	Percentage int | ||||
| } | ||||
|  | ||||
| func getBatteryMessage(msg map[string]string) BatteryMessage { | ||||
| 	plugged, _ := strconv.ParseBool(msg["live"]) | ||||
| 	powersave, _ := strconv.ParseBool(msg["powersave"]) | ||||
| 	percentage, _ := strconv.Atoi(msg["value"]) | ||||
| 	batteryMessage := BatteryMessage{ | ||||
| 		Plugged:    plugged, | ||||
| 		Powersave:  powersave, | ||||
| 		Percentage: percentage, | ||||
| 	} | ||||
|  | ||||
| 	return batteryMessage | ||||
| } | ||||
|  | ||||
| func getNewContact(msg map[string]string) Contact { | ||||
| 	contact := Contact{ | ||||
| 		Jid:    msg["jid"], | ||||
| 		Notify: msg["notify"], | ||||
| 	} | ||||
|  | ||||
| 	return contact | ||||
| } | ||||
|  | ||||
| func ParseNodeMessage(msg binary.Node) interface{} { | ||||
| 	switch msg.Description { | ||||
| 	case "battery": | ||||
| 		return getBatteryMessage(msg.Attributes) | ||||
| 	case "user": | ||||
| 		return getNewContact(msg.Attributes) | ||||
| 	default: | ||||
| 		//cannot match message | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
							
								
								
									
										32
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/Rhymen/go-whatsapp/read.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -5,13 +5,15 @@ import ( | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/Rhymen/go-whatsapp/binary" | ||||
| 	"github.com/Rhymen/go-whatsapp/crypto/cbc" | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func (wac *Conn) readPump() { | ||||
| @@ -27,7 +29,9 @@ func (wac *Conn) readPump() { | ||||
| 	for { | ||||
| 		readerFound := make(chan struct{}) | ||||
| 		go func() { | ||||
| 			msgType, reader, readErr = wac.ws.conn.NextReader() | ||||
| 			if wac.ws != nil { | ||||
| 				msgType, reader, readErr = wac.ws.conn.NextReader() | ||||
| 			} | ||||
| 			close(readerFound) | ||||
| 		}() | ||||
| 		select { | ||||
| @@ -59,6 +63,10 @@ func (wac *Conn) processReadData(msgType int, msg []byte) error { | ||||
| 		data[0] = "!" | ||||
| 	} | ||||
|  | ||||
| 	if len(data) == 2 && len(data[1]) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if len(data) != 2 || len(data[1]) == 0 { | ||||
| 		return ErrInvalidWsData | ||||
| 	} | ||||
| @@ -75,10 +83,8 @@ func (wac *Conn) processReadData(msgType int, msg []byte) error { | ||||
| 		// chan string to something like chan map[string]interface{}. The unmarshalling | ||||
| 		// in several places, especially in session.go, would then be gone. | ||||
| 		listener <- data[1] | ||||
|  | ||||
| 		wac.listener.Lock() | ||||
| 		delete(wac.listener.m, data[0]) | ||||
| 		wac.listener.Unlock() | ||||
| 		close(listener) | ||||
| 		wac.removeListener(data[0]) | ||||
| 	} else if msgType == websocket.BinaryMessage { | ||||
| 		wac.loginSessionLock.RLock() | ||||
| 		sess := wac.session | ||||
| @@ -104,16 +110,16 @@ func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) { | ||||
| 		var response struct { | ||||
| 			Status int `json:"status"` | ||||
| 		} | ||||
| 		err := json.Unmarshal(msg, &response) | ||||
| 		if err == nil { | ||||
| 			if response.Status == 404 { | ||||
|  | ||||
| 		if err := json.Unmarshal(msg, &response); err == nil { | ||||
| 			if response.Status == http.StatusNotFound { | ||||
| 				return nil, ErrServerRespondedWith404 | ||||
| 			} | ||||
| 			return nil, errors.New(fmt.Sprintf("server responded with %d", response.Status)) | ||||
| 		} else { | ||||
| 			return nil, ErrInvalidServerResponse | ||||
| 		} | ||||
|  | ||||
| 		return nil, ErrInvalidServerResponse | ||||
|  | ||||
| 	} | ||||
| 	h2.Write([]byte(msg[32:])) | ||||
| 	if !hmac.Equal(h2.Sum(nil), msg[:32]) { | ||||
|   | ||||
							
								
								
									
										22
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/Rhymen/go-whatsapp/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -18,7 +18,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| //represents the WhatsAppWeb client version | ||||
| var waVersion = []int{0, 4, 2080} | ||||
| var waVersion = []int{2, 2039, 9} | ||||
|  | ||||
| /* | ||||
| Session contains session individual information. To be able to resume the connection without scanning the qr code | ||||
| @@ -141,7 +141,7 @@ 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, version string) error { | ||||
| func (wac *Conn) SetClientName(long, short string, 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") | ||||
| 	} | ||||
| @@ -231,7 +231,12 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) { | ||||
| 		return session, fmt.Errorf("error decoding login resp: %v\n", err) | ||||
| 	} | ||||
|  | ||||
| 	ref := resp["ref"].(string) | ||||
| 	var ref string | ||||
| 	if rref, ok := resp["ref"].(string); ok { | ||||
| 		ref = rref | ||||
| 	} else { | ||||
| 		return session, fmt.Errorf("error decoding login resp: invalid resp['ref']\n") | ||||
| 	} | ||||
|  | ||||
| 	priv, pub, err := curve25519.GenerateKey() | ||||
| 	if err != nil { | ||||
| @@ -390,9 +395,11 @@ func (wac *Conn) Restore() error { | ||||
| 		} | ||||
|  | ||||
| 		if int(resp["status"].(float64)) != 200 { | ||||
| 			wac.timeTag = "" | ||||
| 			return fmt.Errorf("init responded with %d", resp["status"]) | ||||
| 		} | ||||
| 	case <-time.After(wac.msgTimeout): | ||||
| 		wac.timeTag = "" | ||||
| 		return fmt.Errorf("restore session init timed out") | ||||
| 	} | ||||
|  | ||||
| @@ -401,10 +408,11 @@ func (wac *Conn) Restore() error { | ||||
| 	select { | ||||
| 	case r1 := <-s1: | ||||
| 		if err := json.Unmarshal([]byte(r1), &connResp); err != nil { | ||||
| 			wac.timeTag = "" | ||||
| 			return fmt.Errorf("error decoding s1 message: %v\n", err) | ||||
| 		} | ||||
| 	case <-time.After(wac.msgTimeout): | ||||
|  | ||||
| 		wac.timeTag = "" | ||||
| 		//check for an error message | ||||
| 		select { | ||||
| 		case r := <-loginChan: | ||||
| @@ -429,15 +437,18 @@ func (wac *Conn) Restore() error { | ||||
| 		wac.listener.Unlock() | ||||
|  | ||||
| 		if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil { | ||||
| 			wac.timeTag = "" | ||||
| 			return fmt.Errorf("error resolving challenge: %v\n", err) | ||||
| 		} | ||||
|  | ||||
| 		select { | ||||
| 		case r := <-s2: | ||||
| 			if err := json.Unmarshal([]byte(r), &connResp); err != nil { | ||||
| 				wac.timeTag = "" | ||||
| 				return fmt.Errorf("error decoding s2 message: %v\n", err) | ||||
| 			} | ||||
| 		case <-time.After(wac.msgTimeout): | ||||
| 			wac.timeTag = "" | ||||
| 			return fmt.Errorf("restore session challenge timed out") | ||||
| 		} | ||||
| 	} | ||||
| @@ -447,13 +458,16 @@ func (wac *Conn) Restore() error { | ||||
| 	case r := <-loginChan: | ||||
| 		var resp map[string]interface{} | ||||
| 		if err = json.Unmarshal([]byte(r), &resp); err != nil { | ||||
| 			wac.timeTag = "" | ||||
| 			return fmt.Errorf("error decoding login connResp: %v\n", err) | ||||
| 		} | ||||
|  | ||||
| 		if int(resp["status"].(float64)) != 200 { | ||||
| 			wac.timeTag = "" | ||||
| 			return fmt.Errorf("admin login responded with %d", resp["status"]) | ||||
| 		} | ||||
| 	case <-time.After(wac.msgTimeout): | ||||
| 		wac.timeTag = "" | ||||
| 		return fmt.Errorf("restore session login timed out") | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										75
									
								
								vendor/github.com/Rhymen/go-whatsapp/write.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/Rhymen/go-whatsapp/write.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -15,24 +15,48 @@ import ( | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| func (wac *Conn) addListener(ch chan string, messageTag string) { | ||||
| 	wac.listener.Lock() | ||||
| 	wac.listener.m[messageTag] = ch | ||||
| 	wac.listener.Unlock() | ||||
| } | ||||
|  | ||||
| func (wac *Conn) removeListener(answerMessageTag string) { | ||||
| 	wac.listener.Lock() | ||||
| 	delete(wac.listener.m, answerMessageTag) | ||||
| 	wac.listener.Unlock() | ||||
| } | ||||
|  | ||||
| //writeJson enqueues a json message into the writeChan | ||||
| func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) { | ||||
|  | ||||
| 	ch := make(chan string, 1) | ||||
|  | ||||
| 	wac.writerLock.Lock() | ||||
| 	defer wac.writerLock.Unlock() | ||||
|  | ||||
| 	d, err := json.Marshal(data) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		close(ch) | ||||
| 		return ch, err | ||||
| 	} | ||||
|  | ||||
| 	ts := time.Now().Unix() | ||||
| 	messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount) | ||||
| 	bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d)) | ||||
|  | ||||
| 	ch, err := wac.write(websocket.TextMessage, messageTag, bytes) | ||||
| 	if wac.timeTag == "" { | ||||
| 		tss := fmt.Sprintf("%d", ts) | ||||
| 		wac.timeTag = tss[len(tss)-3:] | ||||
| 	} | ||||
|  | ||||
| 	wac.addListener(ch, messageTag) | ||||
|  | ||||
| 	err = wac.write(websocket.TextMessage, bytes) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		close(ch) | ||||
| 		wac.removeListener(messageTag) | ||||
| 		return ch, err | ||||
| 	} | ||||
|  | ||||
| 	wac.msgCount++ | ||||
| @@ -40,8 +64,12 @@ func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) { | ||||
| } | ||||
|  | ||||
| func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) { | ||||
|  | ||||
| 	ch := make(chan string, 1) | ||||
|  | ||||
| 	if len(messageTag) < 2 { | ||||
| 		return nil, ErrMissingMessageTag | ||||
| 		close(ch) | ||||
| 		return ch, ErrMissingMessageTag | ||||
| 	} | ||||
|  | ||||
| 	wac.writerLock.Lock() | ||||
| @@ -49,16 +77,21 @@ func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, message | ||||
|  | ||||
| 	data, err := wac.encryptBinaryMessage(node) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "encryptBinaryMessage(node) failed") | ||||
| 		close(ch) | ||||
| 		return ch, errors.Wrap(err, "encryptBinaryMessage(node) failed") | ||||
| 	} | ||||
|  | ||||
| 	bytes := []byte(messageTag + ",") | ||||
| 	bytes = append(bytes, byte(metric), byte(flag)) | ||||
| 	bytes = append(bytes, data...) | ||||
|  | ||||
| 	ch, err := wac.write(websocket.BinaryMessage, messageTag, bytes) | ||||
| 	wac.addListener(ch, messageTag) | ||||
|  | ||||
| 	err = wac.write(websocket.BinaryMessage, bytes) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "failed to write message") | ||||
| 		close(ch) | ||||
| 		wac.removeListener(messageTag) | ||||
| 		return ch, errors.Wrap(err, "failed to write message") | ||||
| 	} | ||||
|  | ||||
| 	wac.msgCount++ | ||||
| @@ -66,9 +99,15 @@ func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, message | ||||
| } | ||||
|  | ||||
| func (wac *Conn) sendKeepAlive() error { | ||||
|  | ||||
| 	respChan := make(chan string, 1) | ||||
| 	wac.addListener(respChan, "!") | ||||
|  | ||||
| 	bytes := []byte("?,,") | ||||
| 	respChan, err := wac.write(websocket.TextMessage, "!", bytes) | ||||
| 	err := wac.write(websocket.TextMessage, bytes) | ||||
| 	if err != nil { | ||||
| 		close(respChan) | ||||
| 		wac.removeListener("!") | ||||
| 		return errors.Wrap(err, "error sending keepAlive") | ||||
| 	} | ||||
|  | ||||
| @@ -117,14 +156,10 @@ func (wac *Conn) sendAdminTest() (bool, error) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) { | ||||
| 	var ch chan string | ||||
| 	if answerMessageTag != "" { | ||||
| 		ch = make(chan string, 1) | ||||
| func (wac *Conn) write(messageType int, data []byte) error { | ||||
|  | ||||
| 		wac.listener.Lock() | ||||
| 		wac.listener.m[answerMessageTag] = ch | ||||
| 		wac.listener.Unlock() | ||||
| 	if wac == nil || wac.ws == nil { | ||||
| 		return ErrInvalidWebsocket | ||||
| 	} | ||||
|  | ||||
| 	wac.ws.Lock() | ||||
| @@ -132,14 +167,10 @@ func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (< | ||||
| 	wac.ws.Unlock() | ||||
|  | ||||
| 	if err != nil { | ||||
| 		if answerMessageTag != "" { | ||||
| 			wac.listener.Lock() | ||||
| 			delete(wac.listener.m, answerMessageTag) | ||||
| 			wac.listener.Unlock() | ||||
| 		} | ||||
| 		return nil, errors.Wrap(err, "error writing to websocket") | ||||
| 		return errors.Wrap(err, "error writing to websocket") | ||||
| 	} | ||||
| 	return ch, nil | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) { | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| _vendor/ | ||||
| vendor/ | ||||
| *.out | ||||
| coverage.txt | ||||
| *.test | ||||
							
								
								
									
										84
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.golangci.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.golangci.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| --- | ||||
| linters: | ||||
|   disable-all: true | ||||
|   enable: | ||||
|     - bodyclose | ||||
|     - deadcode | ||||
|     - errcheck | ||||
|     - gochecknoglobals | ||||
|     - goconst | ||||
|     - gocritic | ||||
|     - gofmt | ||||
|     - goimports | ||||
|     - golint | ||||
|     - goprintffuncname | ||||
|     - gosec | ||||
|     - gosimple | ||||
|     - govet | ||||
|     - ineffassign | ||||
|     - interfacer | ||||
|     - maligned | ||||
|     - misspell | ||||
|     - nakedret | ||||
|     - prealloc | ||||
|     - staticcheck | ||||
|     - structcheck | ||||
|     - stylecheck | ||||
|     - typecheck | ||||
|     - unconvert | ||||
|     - unparam | ||||
|     - unused | ||||
|     - varcheck | ||||
|     - whitespace | ||||
|     - wsl | ||||
|     - godot | ||||
|     - asciicheck | ||||
|     - nolintlint | ||||
|     - gofumpt | ||||
|     - goerr113 | ||||
|     - tparallel | ||||
|     - errorlint | ||||
|     - paralleltest | ||||
|  | ||||
| # - wrapcheck # TODO: v3 Fix | ||||
| # - testpackage # TODO: Fix testpackage | ||||
| # - nestif # TODO: Fix nestif | ||||
|  | ||||
| # don't enable: | ||||
| # - depguard | ||||
| # - dogsled | ||||
| # - dupl | ||||
| # - funlen | ||||
| # - gochecknoinits | ||||
| # - gocognit | ||||
| # - gocyclo | ||||
| # - godox | ||||
| # - gomnd | ||||
| # - lll | ||||
| # - rowserrcheck | ||||
| # - scopelint | ||||
| # - gomodguard | ||||
| # - exhaustive | ||||
| # - nlreturn | ||||
| # - gci | ||||
| # - exhaustivestruct | ||||
|  | ||||
| issues: | ||||
|   exclude-rules: | ||||
|     - path: _test\.go | ||||
|       linters: | ||||
|         - gocyclo | ||||
|         - errcheck | ||||
|         - dupl | ||||
|         - gosec | ||||
|  | ||||
|     - linters: | ||||
|         - errcheck | ||||
|       source: ".((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?| | ||||
|         .*printf?|os\\.(Un)?Setenv)." | ||||
|  | ||||
|     - linters: | ||||
|         - stylecheck | ||||
|       text: "ST1003:.*(Ts|ts).*TS" | ||||
|  | ||||
|   exclude-use-default: false | ||||
							
								
								
									
										2
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.markdownlint.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.markdownlint.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| no-hard-tabs: false | ||||
							
								
								
									
										20
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/SevereCloud/vksdk/v2/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| --- | ||||
| language: go | ||||
|  | ||||
| cache: | ||||
|   directories: | ||||
|     - $HOME/.cache/go-build | ||||
|     - $HOME/gopath/pkg/mod | ||||
|  | ||||
| go: | ||||
|   - 1.x | ||||
|  | ||||
| before_script: | ||||
|   - git fetch --depth=1 origin +refs/tags/*:refs/tags/* | ||||
|   - git describe --tags $(git rev-list --tags --max-count=1) --always | ||||
|  | ||||
| script: | ||||
|   - go test -v -race -coverprofile=coverage.txt -covermode=atomic -p=1 ./... | ||||
|  | ||||
| after_success: | ||||
|   - bash <(curl -s https://codecov.io/bash) | ||||
							
								
								
									
										92
									
								
								vendor/github.com/SevereCloud/vksdk/v2/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								vendor/github.com/SevereCloud/vksdk/v2/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| # Contributing | ||||
|  | ||||
| ## Настройки | ||||
|  | ||||
| `vksdk` написан на [Go](https://golang.org/). | ||||
|  | ||||
| Требования: | ||||
|  | ||||
| - [Go 1.13+](https://golang.org/doc/install) | ||||
| - [golangci-lint](https://github.com/golangci/golangci-lint) | ||||
| - [global .gitignore](https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore) | ||||
|  | ||||
| Сделайте fork и клонируйте `vksdk` куда угодно: | ||||
|  | ||||
| ```sh | ||||
| git clone git@github.com:<your name>/vksdk.git | ||||
| ``` | ||||
|  | ||||
| Создайте новую ветку | ||||
|  | ||||
| ```sh | ||||
| git checkout -b <name_of_your_new_branch> | ||||
| ``` | ||||
|  | ||||
| ## Тестирование изменений | ||||
|  | ||||
| Для начала проверьте ваш код с помощью | ||||
| [golangci-lint](https://github.com/golangci/golangci-lint) | ||||
|  | ||||
| ```sh | ||||
| golangci-lint run | ||||
| ``` | ||||
|  | ||||
| Затем можно запускать тесты | ||||
|  | ||||
| ```sh | ||||
| # SERVICE_TOKEN="" | ||||
| # GROUP_TOKEN="" | ||||
| # CLIENT_SECRET="" | ||||
| # USER_TOKEN="" | ||||
| # WIDGET_TOKEN="" | ||||
| # CLIENT_ID="123456" | ||||
| # GROUP_ID="123456" | ||||
| # ACCOUNT_ID="123456" | ||||
| go test ./... | ||||
| ``` | ||||
|  | ||||
| Задавать токены не обязательно - тесты с их использованием будут пропущены. | ||||
| **Не** рекомендуется задавать свой `USER_TOKEN`, так как тесты делают много | ||||
| страшных вещей. | ||||
|  | ||||
| Настройки для VSCode `.vscode/setting.json` | ||||
|  | ||||
| ```json | ||||
| { | ||||
|     "go.testEnvVars": { | ||||
|         "SERVICE_TOKEN": "", | ||||
|         "WIDGET_TOKEN": "", | ||||
|         "GROUP_TOKEN": "", | ||||
|         "CLIENT_SECRET": "", | ||||
|         "USER_TOKEN": "", | ||||
|         "CLIENT_ID": "123456", | ||||
|         "GROUP_ID": "123456", | ||||
|         "ACCOUNT_ID": "123456" | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Создание коммита | ||||
|  | ||||
| Сообщения коммитов должны быть хорошо отформатированы, и чтобы сделать их | ||||
| «стандартизированным», мы используем | ||||
| [Conventional Commits](https://www.conventionalcommits.org/ru). | ||||
|  | ||||
| ```sh | ||||
| git add . | ||||
| git commit | ||||
| ``` | ||||
|  | ||||
| ## Отправьте pull request | ||||
|  | ||||
| Отправьте изменения в ваш репозиторий | ||||
|  | ||||
| ```sh | ||||
| git push origin <name_of_your_new_branch> | ||||
| ``` | ||||
|  | ||||
| Затем откройте [pull request](https://github.com/SevereCloud/vksdk/pulls) | ||||
| с веткой: | ||||
|  | ||||
| - `master` если это багфикс | ||||
| - `dev-v1.2.3` если это новая фича | ||||
							
								
								
									
										21
									
								
								vendor/github.com/SevereCloud/vksdk/v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/SevereCloud/vksdk/v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2020 Daniil Suvorov | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
							
								
								
									
										121
									
								
								vendor/github.com/SevereCloud/vksdk/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								vendor/github.com/SevereCloud/vksdk/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| # VK SDK for Golang | ||||
|  | ||||
| [](https://travis-ci.com/SevereCloud/vksdk) | ||||
| [](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2?tab=subdirectories) | ||||
| [](https://vk.com/dev/) | ||||
| [](https://codecov.io/gh/SevereCloud/vksdk) | ||||
| [](https://vk.me/join/AJQ1d6Or8Q00Y_CSOESfbqGt) | ||||
| [](https://github.com/SevereCloud/vksdk/releases) | ||||
| [](https://github.com/SevereCloud/vksdk/blob/master/LICENSE) | ||||
|  | ||||
| **VK SDK for Golang** ready implementation of the main VK API functions for Go. | ||||
|  | ||||
| [Russian documentation](https://github.com/SevereCloud/vksdk/wiki) | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| Version API 5.126. | ||||
|  | ||||
| - [API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api) | ||||
|   - 400+ methods | ||||
|   - Ability to change the request handler | ||||
|   - Ability to modify HTTP client | ||||
|   - Request Limiter | ||||
|   - Token pool | ||||
| - [Callback API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/callback) | ||||
|   - Tracking tool for users activity in your VK communities | ||||
|   - Supports all events | ||||
|   - Auto setting callback | ||||
| - [Bots Long Poll API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/longpoll-bot) | ||||
|   - Allows you to work with community events in real time | ||||
|   - Supports all events | ||||
|   - Ability to modify HTTP client | ||||
| - [User Long Poll API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/longpoll-user) | ||||
|   - Allows you to work with user events in real time | ||||
|   - Ability to modify HTTP client | ||||
| - [Streaming API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/streaming) | ||||
|   - Receiving public data from VK by specified keywords | ||||
|   - Ability to modify HTTP client | ||||
| - [FOAF](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/foaf) | ||||
|   - Machine-readable ontology describing persons | ||||
|   - Works with users and groups | ||||
|   - The only place to get page creation date | ||||
| - [VK Mini Apps](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/vkapps) | ||||
|   - Checking launch parameters | ||||
|   - Intermediate http handler | ||||
| - [Payments API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/payments) | ||||
|   - Processes payment notifications | ||||
| - [Marusia Skills](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/marusia) | ||||
|   - For creating Marusia Skills | ||||
|  | ||||
| ## Install | ||||
|  | ||||
| ```bash | ||||
| # go mod init mymodulename | ||||
| go get github.com/SevereCloud/vksdk/v2@latest | ||||
| ``` | ||||
|  | ||||
| ## Use by | ||||
|  | ||||
| - [Joe](https://github.com/go-joe/joe) adapter: <https://github.com/tdakkota/joe-vk-adapter> | ||||
| - [Logrus](https://github.com/sirupsen/logrus) hook: <https://github.com/SevereCloud/vkrus> | ||||
|  | ||||
| ### Example | ||||
|  | ||||
| ```go | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/SevereCloud/vksdk/v2/api" | ||||
| 	"github.com/SevereCloud/vksdk/v2/api/params" | ||||
| 	"github.com/SevereCloud/vksdk/v2/events" | ||||
| 	"github.com/SevereCloud/vksdk/v2/longpoll-bot" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	token := "<TOKEN>" // use os.Getenv("TOKEN") | ||||
| 	vk := api.NewVK(token) | ||||
|  | ||||
| 	// get information about the group | ||||
| 	group, err := vk.GroupsGetByID(nil) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// Initializing Long Poll | ||||
| 	lp, err := longpoll.NewLongPoll(vk, group[0].ID) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// New message event | ||||
| 	lp.MessageNew(func(_ context.Context, obj events.MessageNewObject) { | ||||
| 		log.Printf("%d: %s", obj.Message.PeerID, obj.Message.Text) | ||||
|  | ||||
| 		if obj.Message.Text == "ping" { | ||||
| 			b := params.NewMessagesSendBuilder() | ||||
| 			b.Message("pong") | ||||
| 			b.RandomID(0) | ||||
| 			b.PeerID(obj.Message.PeerID) | ||||
|  | ||||
| 			_, err := vk.MessagesSend(b.Params) | ||||
| 			if err != nil { | ||||
| 				log.Fatal(err) | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	// Run Bots Long Poll | ||||
| 	log.Println("Start Long Poll") | ||||
| 	if err := lp.Run(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## LICENSE | ||||
|  | ||||
| [](https://app.fossa.io/projects/git%2Bgithub.com%2FSevereCloud%2Fvksdk?ref=badge_large) | ||||
							
								
								
									
										555
									
								
								vendor/github.com/SevereCloud/vksdk/v2/api/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										555
									
								
								vendor/github.com/SevereCloud/vksdk/v2/api/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,555 @@ | ||||
| # API | ||||
|  | ||||
| [](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api) | ||||
| [](https://vk.com/dev/first_guide) | ||||
|  | ||||
| Данная библиотека поддерживает версию API **5.122**. | ||||
|  | ||||
| ## Запросы | ||||
|  | ||||
| В начале необходимо инициализировать api с помощью [ключа доступа](https://vk.com/dev/access_token): | ||||
|  | ||||
| ```go | ||||
| vk := api.NewVK("<TOKEN>") | ||||
| ``` | ||||
|  | ||||
| ### Запросы к API | ||||
|  | ||||
| - `users.get` -> `vk.UsersGet(api.Params{})` | ||||
| - `groups.get` с extended=1 -> `vk.GroupsGetExtended(api.Params{})` | ||||
|  | ||||
| Список всех методов можно найти на | ||||
| [данной странице](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api?tab=doc#VK). | ||||
|  | ||||
| Пример запроса [`users.get`](https://vk.com/dev/users.get) | ||||
|  | ||||
| ```go | ||||
| users, err := vk.UsersGet(api.Params{ | ||||
| 	"user_ids": 1, | ||||
| }) | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Параметры | ||||
|  | ||||
| [](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api/params) | ||||
|  | ||||
| Модуль params предназначен для генерации параметров запроса. | ||||
|  | ||||
| ```go | ||||
| // import "github.com/SevereCloud/vksdk/v2/api/params" | ||||
|  | ||||
| b := params.NewMessageSendBuilder() | ||||
| b.PeerID(123) | ||||
| b.Random(0) | ||||
| b.DontParseLinks(false) | ||||
| b.Message("Test message") | ||||
|  | ||||
| res, err = api.MessageSend(b.Params) | ||||
| ``` | ||||
|  | ||||
| ### Обработка ошибок | ||||
|  | ||||
| [](https://vk.com/dev/errors) | ||||
|  | ||||
| Обработка ошибок полностью поддерживает методы | ||||
| [go 1.13](https://blog.golang.org/go1.13-errors) | ||||
|  | ||||
| ```go | ||||
| if errors.Is(err, api.ErrAuth) { | ||||
| 	log.Println("User authorization failed") | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ```go | ||||
| var e *api.Error | ||||
| if errors.As(err, &e) { | ||||
| 	switch e.Code { | ||||
| 	case api.ErrCaptcha: | ||||
| 		log.Println("Требуется ввод кода с картинки (Captcha)") | ||||
| 		log.Printf("sid %s img %s", e.CaptchaSID, e.CaptchaImg) | ||||
| 	case 1: | ||||
| 		log.Println("Код ошибки 1") | ||||
| 	default: | ||||
| 		log.Printf("Ошибка %d %s", e.Code, e.Text) | ||||
| 	} | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Для Execute существует отдельная ошибка `ExecuteErrors` | ||||
|  | ||||
| ### Запрос любого метода | ||||
|  | ||||
| Пример запроса [users.get](https://vk.com/dev/users.get) | ||||
|  | ||||
| ```go | ||||
| // Определяем структуру, которую вернет API | ||||
| var response []object.UsersUser | ||||
| var err api.Error | ||||
|  | ||||
| params := api.Params{ | ||||
| 	"user_ids": 1, | ||||
| } | ||||
|  | ||||
| // Делаем запрос | ||||
| err = vk.RequestUnmarshal("users.get", &response, params) | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
|  | ||||
| log.Print(response) | ||||
| ``` | ||||
|  | ||||
| ### Execute | ||||
|  | ||||
| [](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api#VK.Execute) | ||||
| [](https://vk.com/dev/execute) | ||||
|  | ||||
| Универсальный метод, который позволяет запускать последовательность других | ||||
| методов, сохраняя и фильтруя промежуточные результаты. | ||||
|  | ||||
| ```go | ||||
| var response struct { | ||||
| 	Text string `json:"text"` | ||||
| } | ||||
|  | ||||
| err = vk.Execute(`return {text: "hello"};`, &response) | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
|  | ||||
| log.Print(response.Text) | ||||
| ``` | ||||
|  | ||||
| ### Обработчик запросов | ||||
|  | ||||
| Обработчик `vk.Handler` должен возвращать структуру ответа от VK API и ошибку. | ||||
| В качестве параметров принимать название метода и параметры. | ||||
|  | ||||
| ```go | ||||
| vk.Handler = func(method string, params ...api.Params) (api.Response, error) { | ||||
| 	// ... | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Это может потребоваться, если вы можете поставить свой обработчик с | ||||
| [fasthttp](https://github.com/valyala/fasthttp) и логгером. | ||||
|  | ||||
| Стандартный обработчик использует [encoding/json](https://pkg.go.dev/net/http) | ||||
| и [net/http](https://pkg.go.dev/net/http). В стандартном обработчике можно | ||||
| настроить ограничитель запросов и HTTP клиент. | ||||
|  | ||||
| #### Ограничитель запросов | ||||
|  | ||||
| К методам API ВКонтакте (за исключением методов из секций secure и ads) с | ||||
| ключом доступа пользователя или сервисным ключом доступа можно обращаться не | ||||
| чаще 3 раз в секунду. Для ключа доступа сообщества ограничение составляет 20 | ||||
| запросов в секунду. Если логика Вашего приложения подразумевает вызов | ||||
| нескольких методов подряд, имеет смысл обратить внимание на метод execute. Он | ||||
| позволяет совершить до 25 обращений к разным методам в рамках одного запроса. | ||||
|  | ||||
| Для методов секции ads действуют собственные ограничения, ознакомиться с ними | ||||
| Вы можете на [этой странице](https://vk.com/dev/ads_limits). | ||||
|  | ||||
| Максимальное число обращений к методам секции secure зависит от числа | ||||
| пользователей, установивших приложение. Если приложение установило меньше 10 | ||||
| 000 человек, то можно совершать 5 запросов в секунду, до 100 000 — 8 запросов, | ||||
| до 1 000 000 — 20 запросов, больше 1 млн. — 35 запросов в секунду. | ||||
|  | ||||
| Если Вы превысите частотное ограничение, сервер вернет ошибку с кодом | ||||
| **6: "Too many requests per second."**. | ||||
|  | ||||
| С помощью параметра `vk.Limit` можно установить ограничение на определенное | ||||
| количество запросов в секунду | ||||
|  | ||||
| ### HTTP client | ||||
|  | ||||
| В модуле реализована возможность изменять HTTP клиент с помощью параметра | ||||
| `vk.Client` | ||||
|  | ||||
| Пример прокси | ||||
|  | ||||
| ```go | ||||
|  | ||||
| dialer, _ := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct) | ||||
| httpTransport := &http.Transport{ | ||||
| 	Dial:              dialer.Dial, | ||||
| } | ||||
| httpTransport.Dial = dialer.Dial | ||||
|  | ||||
| client := &http.Client{ | ||||
| 	Transport: httpTransport, | ||||
| } | ||||
|  | ||||
| vk.Client = client | ||||
| ``` | ||||
|  | ||||
| ### Ошибка с Captcha | ||||
|  | ||||
| [](https://vk.com/dev/captcha_error) | ||||
|  | ||||
| Если какое-либо действие (например, отправка сообщения) выполняется | ||||
| пользователем слишком часто, то запрос к API может возвращать ошибку | ||||
| "Captcha needed". При этом пользователю понадобится ввести код с изображения | ||||
| и отправить запрос повторно с передачей введенного кода Captcha в параметрах | ||||
| запроса. | ||||
|  | ||||
| **Код ошибки**: 14   | ||||
| **Текст ошибки**: Captcha needed | ||||
|  | ||||
| Если возникает данная ошибка, то в сообщении об ошибке передаются также | ||||
| следующие параметры: | ||||
|  | ||||
| - `err.CaptchaSID` - идентификатор captcha | ||||
| - `err.CaptchaImg` - ссылка на изображение, которое нужно показать | ||||
|   пользователю, чтобы он ввел текст с этого изображения. | ||||
|  | ||||
| В этом случае следует запросить пользователя ввести текст с изображения | ||||
| `err.CaptchaImg` и повторить запрос, добавив в него параметры: | ||||
|  | ||||
| - `captcha_sid` - полученный идентификатор | ||||
| - `captcha_key` - текст, который ввел пользователь | ||||
|  | ||||
| ## Загрузка файлов | ||||
|  | ||||
| [](https://vk.com/dev/upload_files) | ||||
|  | ||||
| ### 1. Загрузка фотографий в альбом | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Файл объемом не более 50 МБ, соотношение сторон не менее 1:20 | ||||
|  | ||||
| Загрузка фотографий в альбом для текущего пользователя: | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadPhoto(albumID, response.Body) | ||||
| ``` | ||||
|  | ||||
| Загрузка фотографий в альбом для группы: | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadPhotoGroup(groupID, albumID, response.Body) | ||||
| ``` | ||||
|  | ||||
| ### 2. Загрузка фотографий на стену | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Файл объемом не более 50 МБ, соотношение сторон не менее 1:20 | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadWallPhoto(response.Body) | ||||
| ``` | ||||
|  | ||||
| Загрузка фотографий в альбом для группы: | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadWallPhotoGroup(groupID, response.Body) | ||||
| ``` | ||||
|  | ||||
| ### 3. Загрузка главной фотографии пользователя или сообщества | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: размер не менее 200x200px, соотношение сторон от 0.25 до 3, | ||||
| сумма высоты и ширины не более 14000px, файл объемом не более 50 МБ, | ||||
| соотношение сторон не менее 1:20. | ||||
|  | ||||
| Загрузка главной фотографии пользователя | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadUserPhoto(file) | ||||
| ``` | ||||
|  | ||||
| Загрузка фотографии пользователя или сообщества с миниатюрой | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadOwnerPhoto(ownerID, squareСrop,file) | ||||
| ``` | ||||
|  | ||||
| Для загрузки главной фотографии сообщества необходимо передать его идентификатор | ||||
| со знаком «минус» в параметре `ownerID`. | ||||
|  | ||||
| Дополнительно Вы можете передать параметр `squareСrop` в формате "x,y,w" (без | ||||
| кавычек), где x и y — координаты верхнего правого угла миниатюры, а w — сторона | ||||
| квадрата. Тогда для фотографии также будет подготовлена квадратная миниатюра. | ||||
|  | ||||
| Загрузка фотографии пользователя или сообщества без миниатюры: | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadOwnerPhoto(ownerID, "", file) | ||||
| ``` | ||||
|  | ||||
| ### 4. Загрузка фотографии в личное сообщение | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: сумма высоты и ширины не более 14000px, файл объемом | ||||
| не более 50 МБ, соотношение сторон не менее 1:20. | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadMessagesPhoto(peerID, file) | ||||
| ``` | ||||
|  | ||||
| ### 5. Загрузка главной фотографии для чата | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: размер не менее 200x200px, соотношение сторон от 0.25 до 3, сумма | ||||
| высоты и ширины не более 14000px, файл объемом не более 50 МБ, соотношение | ||||
| сторон не менее 1:20. | ||||
|  | ||||
| Без обрезки: | ||||
|  | ||||
| ```go | ||||
| messageInfo, err = vk.UploadChatPhoto(peerID, file) | ||||
| ``` | ||||
|  | ||||
| С обрезкой: | ||||
|  | ||||
| ```go | ||||
| messageInfo, err = vk.UploadChatPhotoCrop(peerID, cropX, cropY, cropWidth, file) | ||||
| ``` | ||||
|  | ||||
| ### 6. Загрузка фотографии для товара | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: минимальный размер фото — 400x400px, сумма высоты и ширины | ||||
| не более 14000px, файл объемом не более 50 МБ, соотношение сторон не менее 1:20. | ||||
|  | ||||
| Если Вы хотите загрузить основную фотографию товара, необходимо передать | ||||
| параметр `mainPhoto = true`.  Если фотография не основная, она не будет обрезаться. | ||||
|  | ||||
| Без обрезки: | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadMarketPhoto(groupID, mainPhoto, file) | ||||
| ``` | ||||
|  | ||||
| Основную фотографию с обрезкой: | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadMarketPhotoCrop(groupID, cropX, cropY, cropWidth, file) | ||||
| ``` | ||||
|  | ||||
| ### 7. Загрузка фотографии для подборки товаров | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: минимальный размер фото — 1280x720px, сумма высоты и ширины | ||||
| не более 14000px, файл объемом не более 50 МБ, соотношение сторон не менее 1:20. | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadMarketAlbumPhoto(groupID, file) | ||||
| ``` | ||||
|  | ||||
| ### 9. Загрузка видеозаписей | ||||
|  | ||||
| Допустимые форматы: AVI, MP4, 3GP, MPEG, MOV, MP3, FLV, WMV. | ||||
|  | ||||
| [Параметры](https://vk.com/dev/video.save) | ||||
|  | ||||
| ```go | ||||
| videoUploadResponse, err = vk.UploadVideo(params, file) | ||||
| ``` | ||||
|  | ||||
| После загрузки видеозапись проходит обработку и в списке видеозаписей может | ||||
| появиться спустя некоторое время. | ||||
|  | ||||
| ### 10. Загрузка документов | ||||
|  | ||||
| Допустимые форматы: любые форматы за исключением mp3 и исполняемых файлов. | ||||
| Ограничения: файл объемом не более 200 МБ. | ||||
|  | ||||
| `title` - название файла с расширением | ||||
|  | ||||
| `tags` - метки для поиска | ||||
|  | ||||
| `typeDoc` - тип документа. | ||||
|  | ||||
| - doc - обычный документ; | ||||
| - audio_message - голосовое сообщение | ||||
|  | ||||
| Загрузить документ: | ||||
|  | ||||
| ```go | ||||
| docsDoc, err = vk.UploadDoc(title, tags, file) | ||||
| ``` | ||||
|  | ||||
| Загрузить документ в группу: | ||||
|  | ||||
| ```go | ||||
| docsDoc, err = vk.UploadGroupDoc(groupID, title, tags, file) | ||||
| ``` | ||||
|  | ||||
| Загрузить документ, для последующей отправки документа на стену: | ||||
|  | ||||
| ```go | ||||
| docsDoc, err = vk.UploadWallDoc(title, tags, file) | ||||
| ``` | ||||
|  | ||||
| Загрузить документ в группу, для последующей отправки документа на стену: | ||||
|  | ||||
| ```go | ||||
| docsDoc, err = vk.UploadGroupWallDoc(groupID, title, tags, file) | ||||
| ``` | ||||
|  | ||||
| Загрузить документ в личное сообщение: | ||||
|  | ||||
| ```go | ||||
| docsDoc, err = vk.UploadMessagesDoc(peerID, typeDoc, title, tags, file) | ||||
| ``` | ||||
|  | ||||
| ### 11. Загрузка обложки сообщества | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: минимальный размер фото — 795x200px, сумма высоты и ширины | ||||
| не более 14000px, файл объемом не более 50 МБ. Рекомендуемый размер: 1590x400px. | ||||
| В сутки можно загрузить не более 1500 обложек. | ||||
|  | ||||
| Необходимо указать координаты обрезки фотографии в параметрах | ||||
| `cropX`, `cropY`, `cropX2`, `cropY2`. | ||||
|  | ||||
| ```go | ||||
| photo, err = vk.UploadOwnerCoverPhoto(groupID, cropX, cropY, cropX2, cropY2, file) | ||||
| ``` | ||||
|  | ||||
| ### 12. Загрузка аудиосообщения | ||||
|  | ||||
| Допустимые форматы: Ogg Opus. | ||||
| Ограничения: sample rate 16kHz, variable bitrate 16 kbit/s, длительность | ||||
| не более 5 минут. | ||||
|  | ||||
| ```go | ||||
| docsDoc, err = vk.UploadMessagesDoc(peerID, "audio_message", title, tags, file) | ||||
| ``` | ||||
|  | ||||
| ### 13. Загрузка истории | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: сумма высоты и ширины не более 14000px, файл объемом | ||||
| не более 10МБ. Формат видео: h264 video, aac audio, | ||||
| максимальное разрешение 720х1280, 30fps. | ||||
|  | ||||
| Загрузить историю с фотографией. [Параметры](https://vk.com/dev/stories.getPhotoUploadServer) | ||||
|  | ||||
| ```go | ||||
| uploadInfo, err = vk.UploadStoriesPhoto(params, file) | ||||
| ``` | ||||
|  | ||||
| Загрузить историю с видео. [Параметры](https://vk.com/dev/stories.getVideoUploadServer) | ||||
|  | ||||
| ```go | ||||
| uploadInfo, err = vk.UploadStoriesVideo(params, file) | ||||
| ``` | ||||
|  | ||||
| ### Загрузка фоновой фотографии в опрос | ||||
|  | ||||
| Допустимые форматы: JPG, PNG, GIF. | ||||
| Ограничения: сумма высоты и ширины не более 14000px, файл объемом не более 50 МБ, | ||||
| соотношение сторон не менее 1:20. | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadPollsPhoto(file) | ||||
| ``` | ||||
|  | ||||
| ```go | ||||
| photosPhoto, err = vk.UploadOwnerPollsPhoto(ownerID, file) | ||||
| ``` | ||||
|  | ||||
| Для загрузки фотографии сообщества необходимо передать его идентификатор со | ||||
| знаком «минус» в параметре `ownerID`. | ||||
|  | ||||
| ### Загрузка фотографии для карточки | ||||
|  | ||||
| Для карточек используются квадратные изображения минимальным размером 400х400. | ||||
| В случае загрузки неквадратного изображения, оно будет обрезано до квадратного. | ||||
| Допустимые форматы: JPG, PNG, BMP, TIFF или GIF. | ||||
| Ограничения: файл объемом не более 5 МБ. | ||||
|  | ||||
| ```go | ||||
| photo, err = vk.UploadPrettyCardsPhoto(file) | ||||
| ``` | ||||
|  | ||||
| ### Загрузка обложки для формы | ||||
|  | ||||
| Для форм сбора заявок используются прямоугольные изображения размером 1200х300. | ||||
| В случае загрузки изображения другого размера, оно будет автоматически обрезано | ||||
| до требуемого. | ||||
| Допустимые форматы: JPG, PNG, BMP, TIFF или GIF. | ||||
| Ограничения: файл объемом не более 5 МБ. | ||||
|  | ||||
| ```go | ||||
| photo, err = vk.UploadLeadFormsPhoto(file) | ||||
| ``` | ||||
|  | ||||
| Полученные данные можно использовать в методах | ||||
| [leadForms.create](https://vk.com/dev/leadForms.create) | ||||
| и | ||||
| [leadForms.edit](https://vk.com/dev/leadForms.edit). | ||||
|  | ||||
| Полученные данные можно использовать в методах | ||||
| [prettyCards.create](https://vk.com/dev/prettyCards.create) | ||||
| и | ||||
| [prettyCards.edit](https://vk.com/dev/prettyCards.edit). | ||||
|  | ||||
| ### Загрузки фотографии в коллекцию приложения для виджетов приложений сообществ | ||||
|  | ||||
| `imageType` (string) - тип изображения. | ||||
|  | ||||
| Возможные значения: | ||||
|  | ||||
| - 24x24 | ||||
| - 50x50 | ||||
| - 160x160 | ||||
| - 160x240 | ||||
| - 510x128 | ||||
|  | ||||
| ```go | ||||
| image, err = vk.UploadAppImage(imageType, file) | ||||
| ``` | ||||
|  | ||||
| ### Загрузки фотографии в коллекцию сообщества для виджетов приложений сообществ | ||||
|  | ||||
| `imageType` (string) - тип изображения. | ||||
|  | ||||
| Возможные значения: | ||||
|  | ||||
| - 24x24 | ||||
| - 50x50 | ||||
| - 160x160 | ||||
| - 160x240 | ||||
| - 510x128 | ||||
|  | ||||
| ```go | ||||
| image, err = vk.UploadGroupAppImage(imageType, file) | ||||
| ``` | ||||
|  | ||||
| #### Примеры | ||||
|  | ||||
| Загрузка фотографии в альбом: | ||||
|  | ||||
| ```go | ||||
| response, err := os.Open("photo.jpeg") | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
| defer response.Body.Close() | ||||
|  | ||||
| photo, err = vk.UploadPhoto(albumID, response.Body) | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Загрузка фотографии в альбом из интернета: | ||||
|  | ||||
| ```go | ||||
| response, err := http.Get("https://sun9-45.userapi.com/c638629/v638629852/2afba/o-dvykjSIB4.jpg") | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
| defer response.Body.Close() | ||||
|  | ||||
| photo, err = vk.UploadPhoto(albumID, response.Body) | ||||
| if err != nil { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										196
									
								
								vendor/github.com/SevereCloud/vksdk/v2/api/account.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								vendor/github.com/SevereCloud/vksdk/v2/api/account.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| package api // import "github.com/SevereCloud/vksdk/v2/api" | ||||
|  | ||||
| import ( | ||||
| 	"github.com/SevereCloud/vksdk/v2/object" | ||||
| ) | ||||
|  | ||||
| // AccountBan account.ban. | ||||
| // | ||||
| // https://vk.com/dev/account.ban | ||||
| func (vk *VK) AccountBan(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.ban", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountChangePasswordResponse struct. | ||||
| type AccountChangePasswordResponse struct { | ||||
| 	Token string `json:"token"` | ||||
| } | ||||
|  | ||||
| // AccountChangePassword changes a user password after access is successfully restored with the auth.restore method. | ||||
| // | ||||
| // https://vk.com/dev/account.changePassword | ||||
| func (vk *VK) AccountChangePassword(params Params) (response AccountChangePasswordResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.changePassword", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetActiveOffersResponse struct. | ||||
| type AccountGetActiveOffersResponse struct { | ||||
| 	Count int                   `json:"count"` | ||||
| 	Items []object.AccountOffer `json:"items"` | ||||
| } | ||||
|  | ||||
| // AccountGetActiveOffers returns a list of active ads (offers). | ||||
| // If the user fulfill their conditions, he will be able to get | ||||
| // the appropriate number of votes to his balance. | ||||
| // | ||||
| // https://vk.com/dev/account.getActiveOffers | ||||
| func (vk *VK) AccountGetActiveOffers(params Params) (response AccountGetActiveOffersResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getActiveOffers", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetAppPermissions gets settings of the user in this application. | ||||
| // | ||||
| // https://vk.com/dev/account.getAppPermissions | ||||
| func (vk *VK) AccountGetAppPermissions(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getAppPermissions", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetBannedResponse struct. | ||||
| type AccountGetBannedResponse struct { | ||||
| 	Count int   `json:"count"` | ||||
| 	Items []int `json:"items"` | ||||
| 	object.ExtendedResponse | ||||
| } | ||||
|  | ||||
| // AccountGetBanned returns a user's blacklist. | ||||
| // | ||||
| // https://vk.com/dev/account.getBanned | ||||
| func (vk *VK) AccountGetBanned(params Params) (response AccountGetBannedResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getBanned", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetCountersResponse struct. | ||||
| type AccountGetCountersResponse object.AccountAccountCounters | ||||
|  | ||||
| // AccountGetCounters returns non-null values of user counters. | ||||
| // | ||||
| // https://vk.com/dev/account.getCounters | ||||
| func (vk *VK) AccountGetCounters(params Params) (response AccountGetCountersResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getCounters", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetInfoResponse struct. | ||||
| type AccountGetInfoResponse object.AccountInfo | ||||
|  | ||||
| // AccountGetInfo returns current account info. | ||||
| // | ||||
| // https://vk.com/dev/account.getInfo | ||||
| func (vk *VK) AccountGetInfo(params Params) (response AccountGetInfoResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getInfo", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetProfileInfoResponse struct. | ||||
| type AccountGetProfileInfoResponse object.AccountUserSettings | ||||
|  | ||||
| // AccountGetProfileInfo returns the current account info. | ||||
| // | ||||
| // https://vk.com/dev/account.getProfileInfo | ||||
| func (vk *VK) AccountGetProfileInfo(params Params) (response AccountGetProfileInfoResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getProfileInfo", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountGetPushSettingsResponse struct. | ||||
| type AccountGetPushSettingsResponse object.AccountPushSettings | ||||
|  | ||||
| // AccountGetPushSettings account.getPushSettings Gets settings of push notifications. | ||||
| // | ||||
| // https://vk.com/dev/account.getPushSettings | ||||
| func (vk *VK) AccountGetPushSettings(params Params) (response AccountGetPushSettingsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.getPushSettings", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountRegisterDevice subscribes an iOS/Android/Windows/Mac based device to receive push notifications. | ||||
| // | ||||
| // https://vk.com/dev/account.registerDevice | ||||
| func (vk *VK) AccountRegisterDevice(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.registerDevice", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSaveProfileInfoResponse struct. | ||||
| type AccountSaveProfileInfoResponse struct { | ||||
| 	Changed     object.BaseBoolInt        `json:"changed"` | ||||
| 	NameRequest object.AccountNameRequest `json:"name_request"` | ||||
| } | ||||
|  | ||||
| // AccountSaveProfileInfo edits current profile info. | ||||
| // | ||||
| // https://vk.com/dev/account.saveProfileInfo | ||||
| func (vk *VK) AccountSaveProfileInfo(params Params) (response AccountSaveProfileInfoResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.saveProfileInfo", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSetInfo allows to edit the current account info. | ||||
| // | ||||
| // https://vk.com/dev/account.setInfo | ||||
| func (vk *VK) AccountSetInfo(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.setInfo", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSetNameInMenu sets an application screen name | ||||
| // (up to 17 characters), that is shown to the user in the left menu. | ||||
| // | ||||
| // https://vk.com/dev/account.setNameInMenu | ||||
| func (vk *VK) AccountSetNameInMenu(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.setNameInMenu", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSetOffline marks a current user as offline. | ||||
| // | ||||
| // https://vk.com/dev/account.setOffline | ||||
| func (vk *VK) AccountSetOffline(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.setOffline", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSetOnline marks the current user as online for 5 minutes. | ||||
| // | ||||
| // https://vk.com/dev/account.setOnline | ||||
| func (vk *VK) AccountSetOnline(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.setOnline", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSetPushSettings change push settings. | ||||
| // | ||||
| // https://vk.com/dev/account.setPushSettings | ||||
| func (vk *VK) AccountSetPushSettings(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.setPushSettings", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountSetSilenceMode mutes push notifications for the set period of time. | ||||
| // | ||||
| // https://vk.com/dev/account.setSilenceMode | ||||
| func (vk *VK) AccountSetSilenceMode(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.setSilenceMode", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountUnban account.unban. | ||||
| // | ||||
| // https://vk.com/dev/account.unban | ||||
| func (vk *VK) AccountUnban(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.unban", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AccountUnregisterDevice unsubscribes a device from push notifications. | ||||
| // | ||||
| // https://vk.com/dev/account.unregisterDevice | ||||
| func (vk *VK) AccountUnregisterDevice(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("account.unregisterDevice", &response, params) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										581
									
								
								vendor/github.com/SevereCloud/vksdk/v2/api/ads.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										581
									
								
								vendor/github.com/SevereCloud/vksdk/v2/api/ads.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,581 @@ | ||||
| package api // import "github.com/SevereCloud/vksdk/v2/api" | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
|  | ||||
| 	"github.com/SevereCloud/vksdk/v2/object" | ||||
| ) | ||||
|  | ||||
| // AdsAddOfficeUsersItem struct. | ||||
| type AdsAddOfficeUsersItem struct { | ||||
| 	OK    object.BaseBoolInt | ||||
| 	Error AdsError | ||||
| } | ||||
|  | ||||
| // UnmarshalJSON func. | ||||
| func (r *AdsAddOfficeUsersItem) UnmarshalJSON(data []byte) (err error) { | ||||
| 	if r.OK.UnmarshalJSON(data) != nil { | ||||
| 		return json.Unmarshal(data, &r.Error) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsAddOfficeUsersResponse struct. | ||||
| type AdsAddOfficeUsersResponse []AdsAddOfficeUsersItem | ||||
|  | ||||
| // AdsAddOfficeUsers adds managers and/or supervisors to advertising account. | ||||
| // | ||||
| // https://vk.com/dev/ads.addOfficeUsers | ||||
| func (vk *VK) AdsAddOfficeUsers(params Params) (response AdsAddOfficeUsersResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.addOfficeUsers", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCheckLinkResponse struct. | ||||
| type AdsCheckLinkResponse struct { | ||||
| 	// link status | ||||
| 	Status object.AdsLinkStatus `json:"status"` | ||||
|  | ||||
| 	// (if status = disallowed) — description of the reason | ||||
| 	Description string `json:"description,omitempty"` | ||||
|  | ||||
| 	// (if the end link differs from original and status = allowed) — end link. | ||||
| 	RedirectURL string `json:"redirect_url,omitempty"` | ||||
| } | ||||
|  | ||||
| // AdsCheckLink allows to check the ad link. | ||||
| // | ||||
| // https://vk.com/dev/ads.checkLink | ||||
| func (vk *VK) AdsCheckLink(params Params) (response AdsCheckLinkResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.checkLink", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCreateAdsResponse struct. | ||||
| type AdsCreateAdsResponse []struct { | ||||
| 	ID int `json:"id"` | ||||
| 	AdsError | ||||
| } | ||||
|  | ||||
| // AdsCreateAds creates ads. | ||||
| // | ||||
| // Please note! Maximum allowed number of ads created in one request is 5. | ||||
| // Minimum size of ad audience is 50 people. | ||||
| // | ||||
| // https://vk.com/dev/ads.createAds | ||||
| func (vk *VK) AdsCreateAds(params Params) (response AdsCreateAdsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.createAds", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCreateCampaignsResponse struct. | ||||
| type AdsCreateCampaignsResponse []struct { | ||||
| 	ID int `json:"id"` | ||||
| 	AdsError | ||||
| } | ||||
|  | ||||
| // AdsCreateCampaigns creates advertising campaigns. | ||||
| // | ||||
| // Please note! Allowed number of campaigns created in one request is 50. | ||||
| // | ||||
| // https://vk.com/dev/ads.createCampaigns | ||||
| func (vk *VK) AdsCreateCampaigns(params Params) (response AdsCreateCampaignsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.createCampaigns", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCreateClientsResponse struct. | ||||
| type AdsCreateClientsResponse []struct { | ||||
| 	ID int `json:"id"` | ||||
| 	AdsError | ||||
| } | ||||
|  | ||||
| // AdsCreateClients creates clients of an advertising agency. | ||||
| // | ||||
| // Available only for advertising agencies. | ||||
| // | ||||
| // Please note! Allowed number of clients created in one request is 50. | ||||
| // | ||||
| // https://vk.com/dev/ads.createClients | ||||
| func (vk *VK) AdsCreateClients(params Params) (response AdsCreateClientsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.createClients", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCreateLookalikeRequestResponse struct. | ||||
| type AdsCreateLookalikeRequestResponse struct { | ||||
| 	RequestID int `json:"request_id"` | ||||
| } | ||||
|  | ||||
| // AdsCreateLookalikeRequest creates a request to find a similar audience. | ||||
| // | ||||
| // https://vk.com/dev/ads.createLookalikeRequest | ||||
| func (vk *VK) AdsCreateLookalikeRequest(params Params) (response AdsCreateLookalikeRequestResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.createLookalikeRequest", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCreateTargetGroupResponse struct. | ||||
| type AdsCreateTargetGroupResponse struct { | ||||
| 	ID int `json:"id"` | ||||
| } | ||||
|  | ||||
| // AdsCreateTargetGroup Creates a group to re-target ads for users who visited | ||||
| // advertiser's site (viewed information about the product, registered, etc.). | ||||
| // | ||||
| // When executed successfully this method returns user accounting code on | ||||
| // advertiser's site. You shall add this code to the site page, so users | ||||
| // registered in VK will be added to the created target group after they visit | ||||
| // this page. | ||||
| // | ||||
| // Use ads.importTargetContacts method to import existing user contacts to | ||||
| // the group. | ||||
| // | ||||
| // Please note! Maximum allowed number of groups for one advertising | ||||
| // account is 100. | ||||
| // | ||||
| // https://vk.com/dev/ads.createTargetGroup | ||||
| func (vk *VK) AdsCreateTargetGroup(params Params) (response AdsCreateTargetGroupResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.createTargetGroup", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsCreateTargetPixelResponse struct. | ||||
| type AdsCreateTargetPixelResponse struct { | ||||
| 	ID    int    `json:"id"` | ||||
| 	Pixel string `json:"pixel"` | ||||
| } | ||||
|  | ||||
| // AdsCreateTargetPixel Creates retargeting pixel. | ||||
| // | ||||
| // Method returns pixel code for users accounting on the advertiser site. | ||||
| // Authorized VK users who visited the page with pixel code on it will be | ||||
| // added to retargeting audience with corresponding rules. You can also use | ||||
| // Open API, ads.importTargetContacts method and loading from file. | ||||
| // | ||||
| // Maximum pixels number per advertising account is 25. | ||||
| // | ||||
| // https://vk.com/dev/ads.createTargetPixel | ||||
| func (vk *VK) AdsCreateTargetPixel(params Params) (response AdsCreateTargetPixelResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.createTargetPixel", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsDeleteAdsResponse struct. | ||||
| // | ||||
| // Each response is 0 — deleted successfully, or an error code. | ||||
| type AdsDeleteAdsResponse []ErrorType | ||||
|  | ||||
| // AdsDeleteAds archives ads. | ||||
| // | ||||
| // Warning! Maximum allowed number of ads archived in one request is 100. | ||||
| // | ||||
| // https://vk.com/dev/ads.deleteAds | ||||
| func (vk *VK) AdsDeleteAds(params Params) (response AdsDeleteAdsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.deleteAds", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsDeleteCampaignsResponse struct. | ||||
| // | ||||
| // Each response is 0 — deleted successfully, or an error code. | ||||
| type AdsDeleteCampaignsResponse []ErrorType | ||||
|  | ||||
| // AdsDeleteCampaigns archives advertising campaigns. | ||||
| // | ||||
| // | ||||
| // Warning! Maximum allowed number of campaigns archived in one request is 100. | ||||
| // | ||||
| // https://vk.com/dev/ads.deleteCampaigns | ||||
| func (vk *VK) AdsDeleteCampaigns(params Params) (response AdsDeleteCampaignsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.deleteCampaigns", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsDeleteClientsResponse struct. | ||||
| // | ||||
| // Each response is 0 — deleted successfully, or an error code. | ||||
| type AdsDeleteClientsResponse []ErrorType | ||||
|  | ||||
| // AdsDeleteClients archives clients of an advertising agency. | ||||
| // | ||||
| // Available only for advertising agencies. | ||||
| // | ||||
| // Please note! Maximum allowed number of clients edited in one request is 10. | ||||
| // | ||||
| // https://vk.com/dev/ads.deleteClients | ||||
| func (vk *VK) AdsDeleteClients(params Params) (response AdsDeleteClientsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.deleteClients", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsDeleteTargetGroup deletes target group. | ||||
| // | ||||
| // https://vk.com/dev/ads.deleteTargetGroup | ||||
| func (vk *VK) AdsDeleteTargetGroup(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.deleteTargetGroup", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsDeleteTargetPixel deletes target pixel. | ||||
| // | ||||
| // https://vk.com/dev/ads.deleteTargetPixel | ||||
| func (vk *VK) AdsDeleteTargetPixel(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.deleteTargetPixel", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsGetAccountsResponse struct. | ||||
| type AdsGetAccountsResponse []object.AdsAccount | ||||
|  | ||||
| // AdsGetAccounts returns a list of advertising accounts. | ||||
| // | ||||
| // https://vk.com/dev/ads.getAccounts | ||||
| func (vk *VK) AdsGetAccounts(params Params) (response AdsGetAccountsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.getAccounts", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsGetAdsResponse struct. | ||||
| type AdsGetAdsResponse []object.AdsAd | ||||
|  | ||||
| // AdsGetAds returns a list of ads. | ||||
| // | ||||
| // https://vk.com/dev/ads.getAds | ||||
| func (vk *VK) AdsGetAds(params Params) (response AdsGetAdsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.getAds", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsGetAdsLayoutResponse struct. | ||||
| type AdsGetAdsLayoutResponse []object.AdsAdLayout | ||||
|  | ||||
| // AdsGetAdsLayout returns descriptions of ad layouts. | ||||
| // | ||||
| // https://vk.com/dev/ads.getAdsLayout | ||||
| func (vk *VK) AdsGetAdsLayout(params Params) (response AdsGetAdsLayoutResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.getAdsLayout", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // TODO: AdsGetAdsTargetingResponse struct. | ||||
| // type AdsGetAdsTargetingResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetAdsTargeting ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getAdsTargeting | ||||
| // func (vk *VK) AdsGetAdsTargeting(params Params) (response AdsGetAdsTargetingResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getAdsTargeting", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetBudgetResponse struct. | ||||
| // type AdsGetBudgetResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetBudget ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getBudget | ||||
| // func (vk *VK) AdsGetBudget(params Params) (response AdsGetBudgetResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getBudget", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetCampaignsResponse struct. | ||||
| // type AdsGetCampaignsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetCampaigns ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getCampaigns | ||||
| // func (vk *VK) AdsGetCampaigns(params Params) (response AdsGetCampaignsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getCampaigns", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetCategoriesResponse struct. | ||||
| // type AdsGetCategoriesResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetCategories ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getCategories | ||||
| // func (vk *VK) AdsGetCategories(params Params) (response AdsGetCategoriesResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getCategories", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetClientsResponse struct. | ||||
| // type AdsGetClientsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetClients ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getClients | ||||
| // func (vk *VK) AdsGetClients(params Params) (response AdsGetClientsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getClients", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetDemographicsResponse struct. | ||||
| // type AdsGetDemographicsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetDemographics ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getDemographics | ||||
| // func (vk *VK) AdsGetDemographics(params Params) (response AdsGetDemographicsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getDemographics", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetFloodStatsResponse struct. | ||||
| // type AdsGetFloodStatsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetFloodStats ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getFloodStats | ||||
| // func (vk *VK) AdsGetFloodStats(params Params) (response AdsGetFloodStatsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getFloodStats", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetLookalikeRequestsResponse struct. | ||||
| // type AdsGetLookalikeRequestsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetLookalikeRequests ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getLookalikeRequests | ||||
| // func (vk *VK) AdsGetLookalikeRequests(params Params) (response AdsGetLookalikeRequestsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getLookalikeRequests", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // AdsGetMusiciansResponse struct. | ||||
| type AdsGetMusiciansResponse struct { | ||||
| 	Items []object.BaseObjectWithName | ||||
| } | ||||
|  | ||||
| // AdsGetMusicians returns a list of musicians. | ||||
| // | ||||
| // https://vk.com/dev/ads.getMusicians | ||||
| func (vk *VK) AdsGetMusicians(params Params) (response AdsGetMusiciansResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.getMusicians", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // TODO: AdsGetOfficeUsersResponse struct. | ||||
| // type AdsGetOfficeUsersResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetOfficeUsers ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getOfficeUsers | ||||
| // func (vk *VK) AdsGetOfficeUsers(params Params) (response AdsGetOfficeUsersResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getOfficeUsers", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetPostsReachResponse struct. | ||||
| // type AdsGetPostsReachResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetPostsReach ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getPostsReach | ||||
| // func (vk *VK) AdsGetPostsReach(params Params) (response AdsGetPostsReachResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getPostsReach", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetRejectionReasonResponse struct. | ||||
| // type AdsGetRejectionReasonResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetRejectionReason ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getRejectionReason | ||||
| // func (vk *VK) AdsGetRejectionReason(params Params) (response AdsGetRejectionReasonResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getRejectionReason", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetStatisticsResponse struct. | ||||
| // type AdsGetStatisticsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetStatistics ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getStatistics | ||||
| // func (vk *VK) AdsGetStatistics(params Params) (response AdsGetStatisticsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getStatistics", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetSuggestionsResponse struct. | ||||
| // type AdsGetSuggestionsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetSuggestions ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getSuggestions | ||||
| // func (vk *VK) AdsGetSuggestions(params Params) (response AdsGetSuggestionsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getSuggestions", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // AdsGetTargetGroupsResponse struct. | ||||
| type AdsGetTargetGroupsResponse []object.AdsTargetGroup | ||||
|  | ||||
| // AdsGetTargetGroups returns a list of target groups. | ||||
| // | ||||
| // https://vk.com/dev/ads.getTargetGroups | ||||
| func (vk *VK) AdsGetTargetGroups(params Params) (response AdsGetTargetGroupsResponse, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.getTargetGroups", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // TODO: AdsGetTargetPixelsResponse struct. | ||||
| // type AdsGetTargetPixelsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetTargetPixels ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getTargetPixels | ||||
| // func (vk *VK) AdsGetTargetPixels(params Params) (response AdsGetTargetPixelsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getTargetPixels", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetTargetingStatsResponse struct. | ||||
| // type AdsGetTargetingStatsResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetTargetingStats ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getTargetingStats | ||||
| // func (vk *VK) AdsGetTargetingStats(params Params) (response AdsGetTargetingStatsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getTargetingStats", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetUploadURLResponse struct. | ||||
| // type AdsGetUploadURLResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetUploadURL ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getUploadURL | ||||
| // func (vk *VK) AdsGetUploadURL(params Params) (response AdsGetUploadURLResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getUploadURL", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsGetVideoUploadURLResponse struct. | ||||
| // type AdsGetVideoUploadURLResponse struct{} | ||||
|  | ||||
| // TODO: AdsGetVideoUploadURL ... | ||||
| // | ||||
| // https://vk.com/dev/ads.getVideoUploadURL | ||||
| // func (vk *VK) AdsGetVideoUploadURL(params Params) (response AdsGetVideoUploadURLResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.getVideoUploadURL", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsImportTargetContactsResponse struct. | ||||
| // type AdsImportTargetContactsResponse struct{} | ||||
|  | ||||
| // TODO: AdsImportTargetContacts ... | ||||
| // | ||||
| // https://vk.com/dev/ads.importTargetContacts | ||||
| // func (vk *VK) AdsImportTargetContacts(params Params) (response AdsImportTargetContactsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.importTargetContacts", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsRemoveOfficeUsersResponse struct. | ||||
| // type AdsRemoveOfficeUsersResponse struct{} | ||||
|  | ||||
| // TODO: AdsRemoveOfficeUsers ... | ||||
| // | ||||
| // https://vk.com/dev/ads.removeOfficeUsers | ||||
| // func (vk *VK) AdsRemoveOfficeUsers(params Params) (response AdsRemoveOfficeUsersResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.removeOfficeUsers", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // AdsRemoveTargetContacts accepts the request to exclude the advertiser's | ||||
| // contacts from the retargeting audience. | ||||
| // | ||||
| // The maximum allowed number of contacts to be excluded by a single | ||||
| // request is 1000. | ||||
| // | ||||
| // Contacts are excluded within a few hours of the request. | ||||
| // | ||||
| // https://vk.com/dev/ads.removeTargetContacts | ||||
| func (vk *VK) AdsRemoveTargetContacts(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.removeTargetContacts", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // TODO: AdsSaveLookalikeRequestResultResponse struct. | ||||
| // type AdsSaveLookalikeRequestResultResponse struct{} | ||||
|  | ||||
| // TODO: AdsSaveLookalikeRequestResult ... | ||||
| // | ||||
| // https://vk.com/dev/ads.saveLookalikeRequestResult | ||||
| // func (vk *VK) AdsSaveLookalikeRequestResult(params Params) ( | ||||
| // 		response AdsSaveLookalikeRequestResultResponse, | ||||
| // 		err error, | ||||
| // 	) { | ||||
| // 	err = vk.RequestUnmarshal("ads.saveLookalikeRequestResult", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsShareTargetGroupResponse struct. | ||||
| // type AdsShareTargetGroupResponse struct{} | ||||
|  | ||||
| // TODO: AdsShareTargetGroup ... | ||||
| // | ||||
| // https://vk.com/dev/ads.shareTargetGroup | ||||
| // func (vk *VK) AdsShareTargetGroup(params Params) (response AdsShareTargetGroupResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.shareTargetGroup", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsUpdateAdsResponse struct. | ||||
| // type AdsUpdateAdsResponse struct{} | ||||
|  | ||||
| // TODO: AdsUpdateAds ... | ||||
| // | ||||
| // https://vk.com/dev/ads.updateAds | ||||
| // func (vk *VK) AdsUpdateAds(params Params) (response AdsUpdateAdsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.updateAds", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsUpdateCampaignsResponse struct. | ||||
| // type AdsUpdateCampaignsResponse struct{} | ||||
|  | ||||
| // TODO: AdsUpdateCampaigns ... | ||||
| // | ||||
| // https://vk.com/dev/ads.updateCampaigns | ||||
| // func (vk *VK) AdsUpdateCampaigns(params Params) (response AdsUpdateCampaignsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.updateCampaigns", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // TODO: AdsUpdateClientsResponse struct. | ||||
| // type AdsUpdateClientsResponse struct{} | ||||
|  | ||||
| // TODO: AdsUpdateClients ... | ||||
| // | ||||
| // https://vk.com/dev/ads.updateClients | ||||
| // func (vk *VK) AdsUpdateClients(params Params) (response AdsUpdateClientsResponse, err error) { | ||||
| // 	err = vk.RequestUnmarshal("ads.updateClients", &response, params) | ||||
| // 	return | ||||
| // } | ||||
|  | ||||
| // AdsUpdateTargetGroup edits target group. | ||||
| // | ||||
| // https://vk.com/dev/ads.updateTargetGroup | ||||
| func (vk *VK) AdsUpdateTargetGroup(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads.updateTargetGroup", &response, params) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // AdsUpdateTargetPixel edits target pixel. | ||||
| // | ||||
| // https://vk.com/dev/ads.updateTargetPixel | ||||
| func (vk *VK) AdsUpdateTargetPixel(params Params) (response int, err error) { | ||||
| 	err = vk.RequestUnmarshal("ads. updateTargetPixel", &response, params) | ||||
| 	return | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user