Compare commits

..

100 Commits

Author SHA1 Message Date
Wim
afcd362cd1 Release v1.22.0 (#1385) 2021-02-01 23:44:17 +01:00
Wim
0452be0cb3 Update vendor (#1384) 2021-02-01 21:29:04 +01:00
Wim
1624f10773 Pick up all the webhooks (discord) (#1383) 2021-02-01 20:44:34 +01:00
Ivanik
8764be7461 Add vk bridge (#1372)
* Add vk bridge

* Vk bridge attachments

* Vk bridge forwarded messages

* Vk bridge sample config and code cleanup

* Vk bridge add vendor

* Vk bridge message edit

* Vk bridge: fix fetching names of other bots

* Vk bridge: code cleanup

* Vk bridge: fix shadows declaration

* Vk bridge: remove UseFileURL
2021-01-29 00:25:14 +01:00
Wim
5dd15ef8e7 Add an even more debug option (discord) (#1368)
Enable discordgo debugging with debuglevel=1 under the [discord.xxx] section, for even more debugging fun.
2021-01-23 00:09:56 +01:00
Alexander
4ac6366706 Allow the XMPP bridge to use slack compatible webhooks (xmpp) (#1364)
* Add mod_slack_webhook support to the XMPP bridge

* Replace b.webhookURL with b.GetString

* Do not return a message ID on webhook POST

* Add the XMPP webhook to the sample configuration
2021-01-21 22:50:04 +01:00
Wim
adc0912efa Update README (#1362) 2021-01-15 23:21:48 +01:00
Wim
536823ce55 Optimize Dockerfile (#1361) 2021-01-15 22:44:01 +01:00
Peter Dave Hello
207cd24edb Unify/sync apk index cache control in Dockerfile (#1352)
The second stage is using a single `apk` command with `--no-cache` which is better.
2021-01-14 23:48:02 +01:00
Paul
b039da1eba Add jpe as valid image filename extension (telegram) (#1360) 2021-01-14 23:42:13 +01:00
Qais Patankar
8fcd0f3b6f Improve Markdown in transmitter docs (discord) (#1351) 2021-01-03 18:57:06 +00:00
Wim
16fde6935c Rename .oga audio files to .ogg (telegram) (#1349) 2021-01-02 00:55:20 +01:00
Wim
9592cff9fa Update README about unsupported steam chat 2021-01-02 00:12:10 +01:00
Wim
109148988c Bump version 2020-12-31 23:42:41 +01:00
Wim
cf13fff7d2 Release v1.21.0 (#1348) 2020-12-31 22:32:13 +01:00
Qais Patankar
a9d8ac8bc0 Refactor "msg-parent-not-found" to config.ParentIDNotFound (#1347) 2020-12-31 18:01:57 +00:00
Qais Patankar
1a4717b366 Reject cross-channel message references (discord) (#1345)
Discord message references have been designed in a way for this to
support cross-channel or even cross-guild references in the future.

This will ensure the ParentID is *not* set when the message refers to a
message that was sent in a different channel.
2020-12-31 16:21:37 +00:00
Qais Patankar
6cadf12260 Add a prefix handler for unthreaded messages (discord) (#1346) 2020-12-31 16:15:42 +00:00
Wim
19d47784bd Add threading support with token (discord) (#1342)
Webhooks don't support the threading yet, so this is token only.
In discord you can reply on each message of a thread, but this is not possible in mattermost (so some changes added there to make sure we always answer on the rootID of the thread).

Also needs some more testing with slack.

update : It now also uses the token when replying to a thread (even if webhooks are enabled), until webhooks have support for threads.
2020-12-31 16:59:47 +01:00
Qais Patankar
b89102c5fc Fix 'webook' typo in discord/webhook.go (#1344) 2020-12-31 16:51:49 +01:00
Wim
4f20ebead3 Update vendor for next release (#1343) 2020-12-31 14:48:12 +01:00
James Lu
a9f89dbc64 Add support for stateless bridging via draft/relaymsg (irc) (#1339)
* irc: add support for stateless bridging via draft/relaymsg

As discussed at https://github.com/42wim/matterbridge/issues/667#issuecomment-634214165

* irc: handle the draft/relaymsg tag in spoofed messages too

* Apply suggestions from code review

Co-authored-by: Wim <wim@42.be>

* Run gofmt on irc.go

* Document relaymsg in matterbridge.toml.sample

Co-authored-by: Wim <wim@42.be>
2020-12-30 18:21:32 +01:00
wschwab
58ea1e07d2 Update README.md (#1340)
added using `chmod a+x` to make the binary executable in the installation section
2020-12-29 20:42:00 +01:00
Qais Patankar
6de4c7e971 Update webhook documentation (discord) (#1335) 2020-12-17 20:27:52 +01:00
Qais Patankar
03dc51ffa2 Split Bdiscord.Send into handleEventWebhook and handleEventBotUser (discord) 2020-12-13 23:19:48 +01:00
Qais Patankar
aef2dcdfdd Move webhook specific logic to its own file (discord) 2020-12-13 23:19:48 +01:00
Qais Patankar
0494119bf4 Extract maybeGetLocalAvatar into its own function (discord) 2020-12-13 23:19:48 +01:00
Qais Patankar
0a17e21119 Remove WebhookURL support (discord) 2020-12-13 23:19:48 +01:00
Qais Patankar
52e2f926f4 Add initial transmitter implementation (discord)
This has been tested with one webhook in one channel.

Sends, edits and deletions work fine
2020-12-13 23:19:48 +01:00
Qais Patankar
611fb279bc Revert "Disable webhook editing (#1296)" (discord)
This reverts commit c23252ab53.
2020-12-13 23:19:48 +01:00
Gary Kim
41b4e64be9 Update go-nc-talk (nctalk) (#1333)
Signed-off-by: Gary Kim <gary@garykim.dev>
2020-12-10 00:06:27 +01:00
Wim
0d7315249d Update vendor (#1330) 2020-12-06 23:16:02 +01:00
Wim
4913766d58 Parse fencedcode in ParseMarkdown. Fixes #1127 (#1329) 2020-12-06 19:38:32 +01:00
Wim
92da8c7044 Mark messages as read (matrix). Fixes #1317 (#1328) 2020-12-06 17:49:35 +01:00
Wim
9dba3d5385 Update rocketchat vendor (#1327)
Contains fixes for #992 and adds more random ID
2020-12-06 17:23:37 +01:00
Wim
2d3c26a4b2 Implement ratelimiting (matrix). Fixes #1238 (#1326) 2020-12-06 17:18:25 +01:00
Wim
8eba2d3e50 Make handlers run async (irc) (#1325)
This makes the handlers run in a seperate go-routine in girc, and makes
sure that girc isn't blocked on executing PONG requests when
matterbridge takes a long time handling the incoming message.

This can happen when another bridge is in a backoff state where the
backoff time exceeds the IRC ping timeout.
2020-12-05 21:41:45 +01:00
Qais Patankar
a8d4a27de1 Remove locale from golangci misspell (#1324) 2020-12-05 15:22:23 +01:00
Qais Patankar
c42167c6f4 Refactor guild finding code (discord) (#1319) 2020-12-03 22:36:08 +01:00
Sebastian P
44d182e2f9 Add nil checks to text message handling (mumble) (#1321) 2020-12-03 22:25:33 +01:00
Wim
ad95e35687 Rename jfif to jpg (whatsapp). Fixes #1292 2020-11-29 15:37:20 +01:00
Wim
640a9995f4 Refactor handleTextMessage (whatsapp) 2020-11-29 15:37:20 +01:00
Wim
95625f6871 Refactor image downloads (whatsapp) 2020-11-29 15:37:20 +01:00
Wim
2c20f72a9c Handle audio downloads (whatsapp) 2020-11-29 15:37:20 +01:00
Wim
5ad788e768 Handle video downloads (whatsapp) 2020-11-29 15:37:20 +01:00
Wim
ed98c586c6 Add support for deleting messages (whatsapp) 2020-11-29 15:37:20 +01:00
Wim
3e865708d6 Refactor/cleanup code (whatsapp) 2020-11-29 15:37:20 +01:00
JeremyRand
c3bcbd63c0 Add UserID to RemoteNickFormat and Tengo (#1308)
* Add UserID to RemoteNickFormat and Tengo

* Use strings.ReplaceAll in gateway.modifyUsername

Fixes a warning from gocritic linter.

* Use Unicode escape sequence instead of raw ZWSP in gateway.modifyUsername

Fixes a warning from stylecheck linter.
2020-11-25 23:54:27 +01:00
Simon THOBY
29e29439ee Show mxids in case of clashing usernames (matrix) (#1309)
Fixes #1302.
2020-11-25 23:51:23 +01:00
Wim
0c19716f44 Join on invite (irc). Fixes #1231 (#1306) 2020-11-22 22:44:15 +01:00
Wim
b24e1bafa1 Add support for irc to irc notice (irc). Fixes #754 (#1305) 2020-11-22 22:21:02 +01:00
Wim
64b899ac89 Retry until we have contacts (whatsapp). Fixes #1122 (#1304) 2020-11-22 21:42:54 +01:00
Wim
aa274e5ab7 Update discordgo fork (#1303) 2020-11-22 19:21:34 +01:00
Wim
7b3eaf3ccf Bump version 2020-11-22 18:55:21 +01:00
Wim
1a5353d768 Release v1.20.0 (#1298) 2020-11-22 17:27:33 +01:00
Wim
eff41759bc Add extra debug to log time spent sending a message per bridge (#1299) 2020-11-22 17:20:20 +01:00
Wim
c23252ab53 Disable webhook editing (discord) (#1296)
See https://github.com/42wim/matterbridge/issues/1255 and
https://github.com/qaisjp/go-discord-irc/issues/57

Webhook edits gets ratelimited which cause other problems with
matterbridge. Disabling for now.
2020-11-22 17:18:48 +01:00
Simon THOBY
1a3c57a031 Send the display name instead of the user name (matrix) (#1282)
* matrix: send the display name (the nickname in matrix parlance) instead of the user name

There is also the option UseUserName (already in use by the discord bridge) to turn back to the old behavior.

* matrix: update displayNames on join events

* matrix: introduce a helper.go file to keep matrix.go size reasonable
2020-11-22 15:57:41 +01:00
Wim
4cc2c914e6 Update vendor (#1297) 2020-11-22 15:55:57 +01:00
Simon THOBY
cbb46293ab Update webhook messages via new endpoint (discord)
When using the webhook, the previous method to edit a message was to
delete the old one via the classical API, and to create a new message
via the webhook. While this works, this means that editing "old" messages
lead to a mess where the chronological order is no longer respected.

This uses an hidden API explained in https://support.discord.com/hc/en-us/community/posts/360034557771
to achieve a proper edition using the webhook API.

The obvious downside of this approach is that since it is an
undocumented API for now, so there is no stability guarantee :/
2020-11-14 04:08:09 +00:00
George
765e00c949 Add NoTLS option to allow plaintext XMPP connections (#1288)
Co-authored-by: George <zhoreeq@users.noreply.github.com>
2020-11-13 23:59:05 +01:00
Simon THOBY
662359908b Allow message edits on matrix (#1286)
based on https://github.com/Half-Shot/matrix-doc/blob/hs/1695-message-edits-proposal/proposals/1695-message-edits.md
2020-11-13 23:42:14 +01:00
Wim
0d99766686 Update slack invite 2020-10-24 21:04:24 +02:00
Wim
ae3bc3358b Allow tengo to drop messages using msgDrop (#1272) 2020-10-21 21:57:14 +02:00
Wim
1e0b4532bd Show deprecate warnings about old tengo settings (#1271) 2020-10-21 20:35:22 +02:00
Wim
4f8b19c686 Add PingDelay option (irc) (#1269) 2020-10-21 01:14:13 +02:00
Wim
84ab223b81 Bump version 2020-10-21 00:59:11 +02:00
Wim
2bb21262d4 Release v1.19.0 (#1268) 2020-10-20 23:34:41 +02:00
Dellle
3188a9ffe6 Add username formatting for all events (matrix) (#1233) 2020-10-20 21:22:31 +02:00
Wim
61569a8610 Add even more debug for irc (#1266) 2020-10-20 00:33:15 +02:00
Wim
075a84427f Update vendor (#1265) 2020-10-19 23:40:00 +02:00
Gary Kim
950f2759bd Add support for downloading files (nctalk) (#1249)
Signed-off-by: Gary Kim <gary@garykim.dev>
2020-10-19 23:16:34 +02:00
Wim
25c82ddf02 Use vendored whatsapp version (#1258) 2020-10-12 00:05:17 +02:00
Wim
2d98df6122 Update vendor (#1257) 2020-10-11 23:07:00 +02:00
Gary Kim
219a5453f9 Append a suffix if user is a guest user (nctalk) (#1250)
Signed-off-by: Gary Kim <gary@garykim.dev>
2020-10-01 22:59:35 +02:00
Sebastian P
214a6a1386 Add Mumble support (#1245) 2020-10-01 22:50:56 +02:00
Wim
e7781dc79c Bump version 2020-10-01 22:46:18 +02:00
Wim
10c4bd1ac8 Create codeql-analysis.yml 2020-10-01 22:02:05 +02:00
Dellle
a42e488e58 Add username for images from WhatsApp (#1232) 2020-09-26 21:32:57 +02:00
Ben Wiederhake
06eb89b05b Matrix: Permit uploading files of other mimetypes (#1237)
This includes at least c-source-files, cpp-source-files,
markdown-files, Rust-files, and plaintext files.
We already allow uploading arbitrary executables. (And javascript-files,
coincidentally.) Not permitting these other text files would be highly unexpected.
2020-09-26 21:28:24 +02:00
Wim
91c58ec027 Bump version 2020-09-05 00:10:30 +02:00
Wim
8b26e42a3a Release v1.18.3 (#1229) 2020-09-05 00:02:37 +02:00
Wim
acca011f15 Use alpine stable for docker 2020-09-04 23:47:03 +02:00
Wim
2f59abdda7 Update vendor (#1228) 2020-09-04 23:29:13 +02:00
Wim
17747a5c88 Remove outdated link. Closes #1224 2020-09-04 22:52:55 +02:00
Wim
cec63546ff Check location of avatarURL (zulip). Fixes #1214 (#1227) 2020-09-04 22:50:57 +02:00
Gary Kim
75f67d2de4 Update nc-talk dependency (#1226) 2020-09-04 22:14:36 +02:00
Tilo Spannagel
712385ffd5 Format rich object strings (nctalk) (#1222)
Signed-off-by: Tilo Spannagel <development@tilosp.de>
2020-08-31 01:49:43 +02:00
Tilo Spannagel
ad90cf09fe Update nc-talk to version 0.1.2 (#1220)
Signed-off-by: Tilo Spannagel <development@tilosp.de>
2020-08-30 15:19:51 +02:00
Tilo Spannagel
f9928c9e25 Switch to upstream gomatrix (#1219)
Signed-off-by: Tilo Spannagel <development@tilosp.de>
2020-08-30 14:01:52 +02:00
Gary Kim
a0741d99b8 Add TLSConfig to nctalk (#1195)
Signed-off-by: Gary Kim <gary@garykim.dev>
2020-08-30 13:49:26 +02:00
NikkyAI
c63f08c811 Sent loopback messages to other websockets as well (api) (#1216) 2020-08-27 22:28:03 +02:00
escoand
58b6c4d277 Handle broadcasts as groups in Whatsapp (#1213)
The current way to get the correct JID of a WhatsApp group is to dump all JIDs to the log and grab the right one. This is working for for groups fine but not for broadcast, as they are not print out.

According to https://www.npmjs.com/package/@noamalffasy/js-whatsapp we have these possibilities:
* Chats: `[country code][phone number]@s.whatsapp.net`
* Groups: `[country code][phone number of creator]-[timestamp of group creation]@g.us`
* Broadcast Channels: `[timestamp of group creation]@broadcast`

But the bridge does currently interprets (and prints) the only second option.
2020-08-26 22:27:50 +02:00
NikkyAI
27c02549c8 Replace gorilla with melody for websocket API (#1205) 2020-08-26 22:27:00 +02:00
Wim
88d371c71c Release v1.18.2 (#1212) 2020-08-25 13:21:53 +02:00
Sandro
b339524613 Add Dockerimage for tgs conversion (#1211)
* Add Dockerfile with tgs to png conversion support

* Add .dockerignore to keep cache busts while testing low
2020-08-25 13:15:24 +02:00
Wim
d5feda5c8a Fix error loop (zulip) (#1210)
Fixes #1047
2020-08-25 00:12:13 +02:00
Wim
2f506425c2 Update whatsapp vendor and fix a panic (#1209)
* Fix another whatsapp panic

* Update whatsapp vendor
2020-08-24 23:35:08 +02:00
Wim
e8167ee3d7 Add link to nctalk in README 2020-08-24 00:50:32 +02:00
Wim
2f5e211065 Bump version 2020-08-24 00:40:26 +02:00
2267 changed files with 140026 additions and 25385 deletions

2
.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
Dockerfile
tgs.Dockerfile

71
.github/workflows/codeql-analysis.yml vendored Normal file
View 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

View File

@@ -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

View File

@@ -1,13 +1,11 @@
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 \

View File

@@ -70,7 +70,7 @@ And more...
- [Changelog](#changelog)
- [FAQ](#faq)
- [Related projects](#related-projects)
- [Articles](#articles)
- [Articles / Tutorials](#articles--tutorials)
- [Thanks](#thanks)
## Features
@@ -91,15 +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)
@@ -109,9 +112,11 @@ And more...
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Minecraft](https://github.com/elytra/MatterLink)
- [Minecraft](https://github.com/raws/mattercraft)
- [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
@@ -121,11 +126,13 @@ 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)
- [Minecraft](https://github.com/raws/mattercraft) (Matterbridge link for Minecraft Server chat)
- [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot)
- [Mattereddit](https://github.com/bonehurtingjuice/mattereddit) (Reddit chat 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
@@ -146,16 +153,16 @@ 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.18.1](https://github.com/42wim/matterbridge/releases/latest)
- Latest stable release [v1.22.0](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
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
@@ -167,16 +174,15 @@ To install or upgrade just download the latest [binary](https://github.com/42wim
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
```
@@ -259,7 +265,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")
@@ -296,19 +302,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://userlinux.net/mattermost-and-matterbridge.html
- <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
@@ -321,24 +331,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 -->
@@ -350,7 +364,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/

View File

@@ -6,9 +6,10 @@ import (
"sync"
"time"
"gopkg.in/olahol/melody.v1"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/gorilla/websocket"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
ring "github.com/zfjagann/golang-ring"
@@ -18,6 +19,7 @@ type API struct {
Messages ring.Ring
sync.RWMutex
*bridge.Config
mrouter *melody.Melody
}
type Message struct {
@@ -33,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"))
@@ -67,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) {
@@ -83,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
}
@@ -131,6 +166,7 @@ func (b *API) handleStream(c echo.Context) error {
}
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 {
@@ -142,51 +178,30 @@ func (b *API) handleStream(c echo.Context) error {
}
}
func (b *API) handleWebsocketMessage(message config.Message) {
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) writePump(conn *websocket.Conn) {
for {
msg := b.Messages.Dequeue()
if msg != nil {
err := conn.WriteJSON(msg)
if err != nil {
break
}
}
}
}
func (b *API) readPump(conn *websocket.Conn) {
for {
message := config.Message{}
err := conn.ReadJSON(&message)
if err != nil {
break
}
b.handleWebsocketMessage(message)
}
}
func (b *API) handleWebsocket(c echo.Context) error {
conn, err := websocket.Upgrade(c.Response().Writer, c.Request(), nil, 1024, 1024)
err := b.mrouter.HandleRequest(c.Response(), c.Request())
if err != nil {
b.Log.Errorf("error in websocket handling '%v'", err)
return err
}
greet := b.getGreeting()
_ = conn.WriteJSON(greet)
go b.writePump(conn)
go b.readPump(conn)
return nil
}

View File

@@ -26,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"`
@@ -44,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
@@ -117,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
@@ -154,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
@@ -213,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

View File

@@ -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")
}
@@ -97,62 +90,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 +229,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 +247,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 +271,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 +285,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 +298,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 +340,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("")
}

View File

@@ -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

View File

@@ -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 {

View 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(nil),
}
}
// 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)
}
}

View 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
View 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
}

View File

@@ -183,7 +183,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,

View File

@@ -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()

View File

@@ -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") && len(username) > 1 {
checksum := crc32.ChecksumIEEE([]byte(msg.Username))
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
}
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,9 +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,
AllowFlood: true,
Debug: debug,
SupportedCaps: map[string][]string{"overdrivenetworks.com/relaymsg": nil, "draft/relaymsg": nil},
})
return i, nil
}
@@ -277,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
@@ -288,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
View 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
}
}
}

View File

@@ -2,9 +2,7 @@ package bmatrix
import (
"bytes"
"encoding/json"
"fmt"
"html"
"mime"
"regexp"
"strings"
@@ -14,16 +12,26 @@ import (
"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
htmlReplacementTag *regexp.Regexp
*bridge.Config
}
@@ -33,11 +41,34 @@ type httpError struct {
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.htmlReplacementTag = regexp.MustCompile("<[^>]*>")
b.RoomMap = make(map[string]string)
b.NicknameMap = make(map[string]NicknameCacheEntry)
return b
}
@@ -49,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
@@ -68,25 +100,18 @@ func (b *Bmatrix) Disconnect() error {
}
func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
retry:
resp, err := b.mc.JoinRoom(channel.Name, "", nil)
if err != nil {
httpErr := handleError(err)
if httpErr.Errcode == "M_LIMIT_EXCEEDED" {
b.Log.Infof("getting ratelimited by matrix, sleeping approx %d seconds before joining %s", httpErr.RetryAfterMs/1000, channel.Name)
time.Sleep((time.Duration(httpErr.RetryAfterMs) * time.Millisecond))
goto retry
return b.retry(func() error {
resp, err := b.mc.JoinRoom(channel.Name, "", nil)
if err != nil {
return err
}
return err
}
b.Lock()
b.RoomMap[resp.RoomID] = channel.Name
b.Unlock()
b.Lock()
b.RoomMap[resp.RoomID] = channel.Name
b.Unlock()
return nil
return nil
})
}
func (b *Bmatrix) Send(msg config.Message) (string, error) {
@@ -95,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
@@ -113,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)
}
}
@@ -134,60 +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
}
var username string
var plainUsername string
// check if we have a </tag>. if we have, we don't escape HTML. #696
if b.htmlTag.MatchString(msg.Username) {
username = msg.Username
// remove the HTML formatting for beautiful push messages #1188
plainUsername = b.htmlReplacementTag.ReplaceAllString(msg.Username, "")
} else {
username = html.EscapeString(msg.Username)
plainUsername = msg.Username
}
// Post normal message with HTML support (eg riot.im)
resp, err := b.mc.SendHTML(channel, plainUsername+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 {
@@ -197,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 {
@@ -210,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,
@@ -245,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)
@@ -255,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())
}
}
}
@@ -329,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
@@ -358,77 +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
}
func handleError(err error) *httpError {
mErr, ok := err.(matrix.HTTPError)
if !ok {
return &httpError{
Err: "not a HTTPError",
}
}
var httpErr httpError
if err := json.Unmarshal(mErr.Contents, &httpErr); err != nil {
return &httpError{
Err: "unmarshal failed",
}
}
return &httpErr
}

View 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, "&lt;MyUser&gt;", uut.formatted)
assert.Equal(t, "<MyUser>", uut.plain)
}

View File

@@ -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{}),
}

View File

@@ -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) {

View File

@@ -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
View 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
View 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 `![alt](` part of markdown images
// `(data:image\/[^)]+)` 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
View 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)
}
}

View File

@@ -2,12 +2,13 @@ package nctalk
import (
"context"
"crypto/tls"
"strconv"
"strings"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
talk "gomod.garykim.dev/nc-talk"
"gomod.garykim.dev/nc-talk/ocs"
"gomod.garykim.dev/nc-talk/room"
"gomod.garykim.dev/nc-talk/user"
@@ -31,8 +32,18 @@ type Broom struct {
func (b *Btalk) Connect() error {
b.Log.Info("Connecting")
b.user = talk.NewUser(b.GetString("Server"), b.GetString("Login"), b.GetString("Password"))
_, err := b.user.Capabilities()
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
@@ -49,8 +60,12 @@ func (b *Btalk) Disconnect() error {
}
func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
tr, err := room.NewTalkRoom(b.user, channel.Name)
if err != nil {
return err
}
newRoom := Broom{
room: talk.NewRoom(b.user, channel.Name),
room: tr,
}
newRoom.ctx, newRoom.ctxCancel = context.WithCancel(context.Background())
c, err := newRoom.room.ReceiveMessages(newRoom.ctx)
@@ -58,8 +73,23 @@ func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
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
@@ -67,9 +97,9 @@ func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
continue
}
remoteMessage := config.Message{
Text: msg.Message,
Text: formatRichObjectString(msg.Message, msg.MessageParameters),
Channel: newRoom.room.Token,
Username: msg.ActorDisplayName,
Username: DisplayName(msg, guestSuffix),
UserID: msg.ActorID,
Account: b.Account,
}
@@ -78,6 +108,15 @@ func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
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
}
@@ -112,3 +151,60 @@ func (b *Btalk) getRoom(token string) *Broom {
}
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
}

View File

@@ -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)
}

View File

@@ -311,6 +311,11 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa
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
}
@@ -384,7 +389,7 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64) string {
Name: fi.Name,
Bytes: *fi.Data,
}
re := regexp.MustCompile(".(jpg|png)$")
re := regexp.MustCompile(".(jpg|jpe|png)$")
if re.MatchString(fi.Name) {
c = tgbotapi.NewPhotoUpload(chatid, file)
} else {

327
bridge/vk/vk.go Normal file
View 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)
}
}
}

View File

@@ -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,11 @@ 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
if message.Info.Source != nil {
if message.Info.Source != nil && message.Info.Source.Participant != nil {
senderJID = *message.Info.Source.Participant
}
}
@@ -101,108 +106,205 @@ 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
}
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
}

View File

@@ -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 {
@@ -114,6 +135,7 @@ func (b *Bwhatsapp) getSenderNotify(senderJid string) string {
if sender, exists := b.users[senderJid]; exists {
return sender.Notify
}
return ""
}
@@ -122,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")
}

View File

@@ -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

View File

@@ -1,8 +1,11 @@
package bxmpp
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
@@ -86,14 +89,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 +112,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 +143,25 @@ 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")+"/"+msg.Channel, "application/json", bytes.NewReader(webhookBody))
resp.Body.Close()
return err
}
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 +178,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 +418,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
}

View File

@@ -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)
}
}

View File

@@ -1,3 +1,146 @@
# v1.22.0
Discord users using autowebhooks are encouraged to upgrade to this release.
## New features
- vk: new protocol added: Add vk support (#1245)
- xmpp: Allow the XMPP bridge to use slack compatible webhooks (xmpp) (#1364)
## Enhancements
- telegram: Rename .oga audio files to .ogg (telegram) (#1349)
- telegram: Add jpe as valid image filename extension (telegram) (#1360)
- discord: Add an even more debug option (discord) (#1368)
- general: Update vendor (#1384)
## Bugfixes
- discord: Pick up all the webhooks (discord) (#1383). Fixes #1353
This release couldn't exist without the following contributors:
@ivanik7, @Polynomdivision, @PeterDaveHello, @Humorhenker, @qaisjp
# v1.21.0
## Breaking Changes
- discord: Remove WebhookURL support (discord) (#1323)
`WebhookURL` global setting for discord is removed and will quit matterbridge.
New `AutoWebhooks=true` setting, which will automatically use (and create, if they do not exist) webhooks inside specific channels. This only works if the bot has Manage Webhooks permission in bridged channels (global permission or as a channel permission override). Backwards compatibility with channel-specific webhooks. More info [here](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample#L862).
## New features
- discord: Create webhooks automatically (#1323)
- discord: Add threading support with token (discord) (#1342)
- irc: Join on invite (irc). Fixes #1231 (#1306)
- irc: Add support for stateless bridging via draft/relaymsg (irc) (#1339)
- whatsapp: Add support for deleting messages (whatsapp) (#1316)
- whatsapp: Handle video downloads (whatsapp) (#1316)
- whatsapp: Handle audio downloads (whatsapp) (#1316)
## Enhancements
- general: Parse fencedcode in ParseMarkdown. Fixes #1127 (#1329)
- discord: Refactor guild finding code (discord) (#1319)
- discord: Add a prefix handler for unthreaded messages (discord) (#1346)
- irc: Add support for irc to irc notice (irc). Fixes #754 (#1305)
- irc: Make handlers run async (irc) (#1325)
- matrix: Show mxids in case of clashing usernames (matrix) (#1309)
- matrix: Implement ratelimiting (matrix). Fixes #1238 (#1326)
- matrix: Mark messages as read (matrix). Fixes #1317 (#1328)
- nctalk: Update go-nc-talk (nctalk) (#1333)
- rocketchat: Update rocketchat vendor (#1327)
- tengo: Add UserID to RemoteNickFormat and Tengo (#1308)
- whatsapp: Retry until we have contacts (whatsapp). Fixes #1122 (#1304)
- whatsapp: Refactor/cleanup code (whatsapp)
- whatsapp: Refactor handleTextMessage (whatsapp)
- whatsapp: Refactor image downloads (whatsapp)
- whatsapp: Rename jfif to jpg (whatsapp). Fixes #1292
## Bugfix
- discord: Reject cross-channel message references (discord) (#1345)
- mumble: Add nil checks to text message handling (mumble) (#1321)
This release couldn't exist without the following contributors:
@nightmared, @qaisjp, @jlu5, @wschwab, @gary-kim, @s3lph, @JeremyRand
# v1.20.0
## Breaking
- matrix: Send the display name instead of the user name (matrix) (#1282)
Matrix now sends the displayname if set instead of the username. If you want to keep the username, add `UseUsername=true` to your matrix config. <https://github.com/42wim/matterbridge/wiki/Settings#useusername-1>
- discord: Disable webhook editing (discord) (#1296)
Because of issues with ratelimiting of webhook editing, this feature is now disabled. If you have multiple discord channels you bridge, you'll need to add a `webhookURL` to the `[gateway.inout.options]`. See <https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample#L1864-L1870> for an example.
## New features
- general: Allow tengo to drop messages using msgDrop (#1272)
- general: Update libraries (whatsapp,markdown,mattermost,ssh-chat)
- irc: Add PingDelay option (irc) (#1269)
- matrix: Allow message edits on matrix (#1286)
- xmpp: add NoTLS option to allow plaintext XMPP connections (#1288)
## Enhancements
- discord: Edit messages via webhook (1287)
- general: Add extra debug to log time spent sending a message per bridge (#1299)
This release couldn't exist without the following contributors:
@nightmared, @zhoreeq
# v1.19.0
## New features
- mumble: new protocol added: Add Mumble support (#1245)
- nctalk: Add support for downloading files (nctalk) (#1249)
- nctalk: Append a suffix if user is a guest user (nctalk) (#1250)
## Enhancements
- irc: Add even more debug for irc (#1266)
- matrix: Add username formatting for all events (matrix) (#1233)
- matrix: Permit uploading files of other mimetypes (#1237)
- whatsapp: Use vendored whatsapp version (#1258)
- whatsapp: Add username for images from WhatsApp (#1232)
This release couldn't exist without the following contributors:
@Dellle, @42wim, @gary-kim, @s3lph, @BenWiederhake
# v1.18.3
## Enhancements
- nctalk: Add TLSConfig to nctalk (#1195)
- whatsapp: Handle broadcasts as groups in Whatsapp #1213
- matrix: switch to upstream gomatrix #1219
- api: support multiple websocket clients #1205
## Bugfix
- general: update vendor
- zulip: Check location of avatarURL (zulip). Fixes #1214 (#1227)
- nctalk: Fix issue with too many open files #1223
- nctalk: Fix mentions #1222
- nctalk: Fix message replays #1220
This release couldn't exist without the following contributors:
@gary-kim, @tilosp, @NikkyAI, @escoand, @42wim
# v1.18.2
## Bugfix
- zulip: Fix error loop (zulip) (#1210)
- whatsapp: Update whatsapp vendor and fix a panic (#1209)
This release couldn't exist without the following contributors:
@SuperSandro2000, @42wim
# v1.18.1
## New features

View 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/bvk.go Normal file
View File

@@ -0,0 +1,11 @@
// +build !novk
package bridgemap
import (
bvk "github.com/42wim/matterbridge/bridge/vk"
)
func init() {
FullMap["vk"] = bvk.New
}

View File

@@ -1,6 +1,7 @@
package gateway
import (
"fmt"
"io/ioutil"
"os"
"regexp"
@@ -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,10 +365,23 @@ 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)
}
@@ -416,9 +431,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 +459,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
@@ -505,7 +538,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 +550,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 +579,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 +599,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 +634,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
}

View File

@@ -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
}

View File

@@ -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)
}
}

59
go.mod
View File

@@ -3,53 +3,60 @@ 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.6.0
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20200922220614-e4a51dfb52e4 // indirect
github.com/SevereCloud/vksdk/v2 v2.9.0
github.com/d5/tengo/v2 v2.6.2
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.4.9
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725
github.com/google/gops v0.3.10
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
github.com/gorilla/schema v1.1.0
github.com/gomarkdown/markdown v0.0.0-20201113031856-722100d81a8e
github.com/google/gops v0.3.14
github.com/gorilla/schema v1.2.0
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/golang-lru v0.5.4
github.com/jpillora/backoff v1.0.0
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da
github.com/labstack/echo/v4 v4.1.16
github.com/labstack/echo/v4 v4.1.17
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.21.2-0.20200718144317-01fe5db6c78d
github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20201206215757-c1d86d75b9f8
github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
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-20200820220548-be5824faa913
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
github.com/mattermost/mattermost-server/v5 v5.25.2
github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561
github.com/mattermost/mattermost-server/v5 v5.30.1
github.com/mattn/godown v0.0.1
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/missdeer/golib v1.0.3
github.com/missdeer/golib v1.0.4
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
github.com/rs/xid v1.2.1
github.com/russross/blackfriday v1.5.2
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.5
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.5.1
github.com/shazow/ssh-chat v1.10.1
github.com/sirupsen/logrus v1.7.0
github.com/slack-go/slack v0.8.0
github.com/spf13/afero v1.3.4 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // 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.3
github.com/yaegashi/msgraph.go v0.1.4
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
golang.org/x/image v0.0.0-20200618115811-c13761719519
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
gomod.garykim.dev/nc-talk v0.0.2
golang.org/x/image v0.0.0-20201208152932-35266b937fa6
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c
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
)
go 1.13
go 1.15

755
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ import (
)
var (
version = "1.18.1"
version = "1.22.0"
githash string
flagConfig = flag.String("conf", "matterbridge.toml", "config file")

View File

@@ -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
@@ -1394,12 +1426,78 @@ ShowTopicChange=false
# 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
###################################################################
#
# WhatsApp
@@ -1540,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"
@@ -1563,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
@@ -1638,7 +1739,7 @@ LogFile="/var/log/matterbridge.log"
#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.
#
@@ -1662,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.
#
@@ -1677,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
#
@@ -1738,6 +1842,8 @@ 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
@@ -1749,6 +1855,8 @@ enable=true
# -------------------------------------------------------------------------------------------------------------------------------------
# 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.
# -------------------------------------------------------------------------------------------------------------------------------------
@@ -1788,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"

38
tgs.Dockerfile Normal file
View 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"]

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1 @@
module github.com/Jeffail/gabs

View File

@@ -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

View File

@@ -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
View 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
View 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=

View File

@@ -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,
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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 (

View File

@@ -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)
}

View File

@@ -48,7 +48,6 @@ type PersonaStateEvent struct {
ClanRank uint32
ClanTag string
OnlineSessionInstances uint32
PublishedSessionId uint32
PersonaSetByUser bool
}

View File

@@ -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.

View File

@@ -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{}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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=

View File

@@ -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" {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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]) {

View File

@@ -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")
}

View File

@@ -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
View File

@@ -0,0 +1,5 @@
_vendor/
vendor/
*.out
coverage.txt
*.test

84
vendor/github.com/SevereCloud/vksdk/v2/.golangci.yml generated vendored Normal file
View 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

View File

@@ -0,0 +1,2 @@
---
no-hard-tabs: false

20
vendor/github.com/SevereCloud/vksdk/v2/.travis.yml generated vendored Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,121 @@
# VK SDK for Golang
[![Build Status](https://travis-ci.com/SevereCloud/vksdk.svg?branch=master)](https://travis-ci.com/SevereCloud/vksdk)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/v2)](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2?tab=subdirectories)
[![VK Developers](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/)
[![codecov](https://codecov.io/gh/SevereCloud/vksdk/branch/master/graph/badge.svg)](https://codecov.io/gh/SevereCloud/vksdk)
[![VK chat](https://img.shields.io/badge/VK%20chat-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.me/join/AJQ1d6Or8Q00Y_CSOESfbqGt)
[![release](https://img.shields.io/github/v/tag/SevereCloud/vksdk?label=release)](https://github.com/SevereCloud/vksdk/releases)
[![license](https://img.shields.io/github/license/SevereCloud/vksdk.svg?maxAge=2592000)](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
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FSevereCloud%2Fvksdk.svg?type=large)](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
View File

@@ -0,0 +1,555 @@
# API
[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/api)](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](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)
}
```
### Параметры
[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/api/params)](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)
```
### Обработка ошибок
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](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
[![PkgGoDev](https://pkg.go.dev/badge/github.com/SevereCloud/vksdk/v2/errors)](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api#VK.Execute)
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](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
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](https://vk.com/dev/captcha_error)
Если какое-либо действие (например, отправка сообщения) выполняется
пользователем слишком часто, то запрос к API может возвращать ошибку
"Captcha needed". При этом пользователю понадобится ввести код с изображения
и отправить запрос повторно с передачей введенного кода Captcha в параметрах
запроса.
**Код ошибки**: 14
**Текст ошибки**: Captcha needed
Если возникает данная ошибка, то в сообщении об ошибке передаются также
следующие параметры:
- `err.CaptchaSID` - идентификатор captcha
- `err.CaptchaImg` - ссылка на изображение, которое нужно показать
пользователю, чтобы он ввел текст с этого изображения.
В этом случае следует запросить пользователя ввести текст с изображения
`err.CaptchaImg` и повторить запрос, добавив в него параметры:
- `captcha_sid` - полученный идентификатор
- `captcha_key` - текст, который ввел пользователь
## Загрузка файлов
[![VK](https://img.shields.io/badge/developers-%234a76a8.svg?logo=VK&logoColor=white)](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
View 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
View 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
}

359
vendor/github.com/SevereCloud/vksdk/v2/api/api.go generated vendored Normal file
View File

@@ -0,0 +1,359 @@
/*
Package api implements VK API.
See more https://vk.com/dev/api_requests
*/
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"bytes"
"context"
"encoding/json"
"fmt"
"mime"
"net/http"
"net/url"
"reflect"
"sync"
"sync/atomic"
"time"
"github.com/SevereCloud/vksdk/v2"
"github.com/SevereCloud/vksdk/v2/internal"
"github.com/SevereCloud/vksdk/v2/object"
)
// Api constants.
const (
Version = vksdk.API
MethodURL = "https://api.vk.com/method/"
)
// VKontakte API methods (except for methods from secure and ads sections)
// with user access key or service access key can be accessed
// no more than 3 times per second. The community access key is limited
// to 20 requests per second.
//
// Maximum amount of calls to the secure section methods depends
// on the app's users amount. If an app has less than
// 10 000 users, 5 requests per second,
// up to 100 000 8 requests,
// up to 1 000 000 20 requests,
// 1 000 000+ 35 requests.
//
// The ads section methods are subject to their own limitations,
// you can read them on this page - https://vk.com/dev/ads_limits
//
// If one of this limits is exceeded, the server will return following error:
// "Too many requests per second". (errors.TooMany).
//
// If your app's logic implies many requests in a row, check the execute method.
// It allows for up to 25 requests for different methods in a single request.
//
// In addition to restrictions on the frequency of calls, there are also
// quantitative restrictions on calling the same type of methods.
//
// After exceeding the quantitative limit, access to a particular method may
// require entering a captcha (see https://vk.com/dev/captcha_error),
// and may also be temporarily restricted (in this case, the server does
// not return a response to the call of a particular method, but handles
// any other requests without problems).
//
// If this error occurs, the following parameters are also passed in
// the error message:
//
// CaptchaSID - identifier captcha.
//
// CaptchaImg - a link to the image that you want to show the user
// to enter text from that image.
//
// In this case, you should ask the user to enter text from
// the CaptchaImg image and repeat the request by adding parameters to it:
//
// captcha_sid - the obtained identifier;
//
// captcha_key - text entered by the user.
//
// More info: https://vk.com/dev/api_requests
const (
LimitUserToken = 3
LimitGroupToken = 20
)
// VK struct.
type VK struct {
accessTokens []string
lastToken uint32
MethodURL string
Version string
Client *http.Client
Limit int
UserAgent string
Handler func(method string, params ...Params) (Response, error)
mux sync.Mutex
lastTime time.Time
rps int
}
// Response struct.
type Response struct {
Response json.RawMessage `json:"response"`
Error Error `json:"error"`
ExecuteErrors ExecuteErrors `json:"execute_errors"`
}
// NewVK returns a new VK.
//
// The VKSDK will use the http.DefaultClient.
// This means that if the http.DefaultClient is modified by other components
// of your application the modifications will be picked up by the SDK as well.
//
// In some cases this might be intended, but it is a better practice
// to create a custom HTTP Client to share explicitly through
// your application. You can configure the VKSDK to use the custom
// HTTP Client by setting the VK.Client value.
//
// This set limit 20 requests per second for one token.
func NewVK(tokens ...string) *VK {
var vk VK
vk.accessTokens = tokens
vk.Version = Version
vk.Handler = vk.defaultHandler
vk.MethodURL = MethodURL
vk.Client = http.DefaultClient
vk.Limit = LimitGroupToken
vk.UserAgent = internal.UserAgent
return &vk
}
// getToken return next token (simple round-robin).
func (vk *VK) getToken() string {
i := atomic.AddUint32(&vk.lastToken, 1)
return vk.accessTokens[(int(i)-1)%len(vk.accessTokens)]
}
// Params type.
type Params map[string]interface{}
// Lang - determines the language for the data to be displayed on. For
// example country and city names. If you use a non-cyrillic language,
// cyrillic symbols will be transliterated automatically.
// Numeric format from account.getInfo is supported as well.
//
// p.Lang(object.LangRU)
//
// See all language code in module object.
func (p Params) Lang(v int) Params {
p["lang"] = v
return p
}
// TestMode allows to send requests from a native app without switching it on
// for all users.
func (p Params) TestMode(v bool) Params {
p["test_mode"] = v
return p
}
// CaptchaSID received ID.
//
// See https://vk.com/dev/captcha_error
func (p Params) CaptchaSID(v string) Params {
p["captcha_sid"] = v
return p
}
// CaptchaKey text input.
//
// See https://vk.com/dev/captcha_error
func (p Params) CaptchaKey(v string) Params {
p["captcha_key"] = v
return p
}
// Confirm parameter.
//
// See https://vk.com/dev/need_confirmation
func (p Params) Confirm(v bool) Params {
p["confirm"] = v
return p
}
// WithContext parameter.
func (p Params) WithContext(ctx context.Context) Params {
p[":context"] = ctx
return p
}
func buildQuery(sliceParams ...Params) (context.Context, url.Values) {
query := url.Values{}
ctx := context.Background()
for _, params := range sliceParams {
for key, value := range params {
if key != ":context" {
query.Set(key, FmtValue(value, 0))
} else {
ctx = value.(context.Context)
}
}
}
return ctx, query
}
// defaultHandler provides access to VK API methods.
func (vk *VK) defaultHandler(method string, sliceParams ...Params) (Response, error) {
u := vk.MethodURL + method
ctx, query := buildQuery(sliceParams...)
attempt := 0
for {
var response Response
attempt++
// Rate limiting
if vk.Limit > 0 {
vk.mux.Lock()
sleepTime := time.Second - time.Since(vk.lastTime)
if sleepTime < 0 {
vk.lastTime = time.Now()
vk.rps = 0
} else if vk.rps == vk.Limit*len(vk.accessTokens) {
time.Sleep(sleepTime)
vk.lastTime = time.Now()
vk.rps = 0
}
vk.rps++
vk.mux.Unlock()
}
rawBody := bytes.NewBufferString(query.Encode())
req, err := http.NewRequestWithContext(ctx, "POST", u, rawBody)
if err != nil {
return response, err
}
req.Header.Set("User-Agent", vk.UserAgent)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := vk.Client.Do(req)
if err != nil {
return response, err
}
mediatype, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if mediatype != "application/json" {
_ = resp.Body.Close()
return response, &InvalidContentType{mediatype}
}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
_ = resp.Body.Close()
return response, err
}
_ = resp.Body.Close()
switch response.Error.Code {
case ErrNoType:
return response, nil
case ErrTooMany:
if attempt < vk.Limit {
continue
}
return response, &response.Error
}
return response, &response.Error
}
}
// Request provides access to VK API methods.
func (vk *VK) Request(method string, sliceParams ...Params) ([]byte, error) {
token := vk.getToken()
reqParams := Params{
"access_token": token,
"v": vk.Version,
}
sliceParams = append(sliceParams, reqParams)
resp, err := vk.Handler(method, sliceParams...)
return resp.Response, err
}
// RequestUnmarshal provides access to VK API methods.
func (vk *VK) RequestUnmarshal(method string, obj interface{}, sliceParams ...Params) error {
rawResponse, err := vk.Request(method, sliceParams...)
if err != nil {
return err
}
return json.Unmarshal(rawResponse, &obj)
}
func fmtReflectValue(value reflect.Value, depth int) string {
switch f := value; value.Kind() {
case reflect.Invalid:
return ""
case reflect.Bool:
return fmtBool(f.Bool())
case reflect.Array, reflect.Slice:
s := ""
for i := 0; i < f.Len(); i++ {
if i > 0 {
s += ","
}
s += FmtValue(f.Index(i).Interface(), depth)
}
return s
case reflect.Ptr:
// pointer to array or slice or struct? ok at top level
// but not embedded (avoid loops)
if depth == 0 && f.Pointer() != 0 {
switch a := f.Elem(); a.Kind() {
case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
return FmtValue(a.Interface(), depth+1)
}
}
}
return fmt.Sprint(value)
}
// FmtValue return vk format string.
func FmtValue(value interface{}, depth int) string {
if value == nil {
return ""
}
switch f := value.(type) {
case bool:
return fmtBool(f)
case object.Attachment:
return f.ToAttachment()
case object.JSONObject:
return f.ToJSON()
case reflect.Value:
return fmtReflectValue(f, depth)
}
return fmtReflectValue(reflect.ValueOf(value), depth)
}

149
vendor/github.com/SevereCloud/vksdk/v2/api/apps.go generated vendored Normal file
View File

@@ -0,0 +1,149 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// AppsDeleteAppRequests deletes all request notifications from the current app.
//
// https://vk.com/dev/apps.deleteAppRequests
func (vk *VK) AppsDeleteAppRequests(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.deleteAppRequests", &response, params)
return
}
// AppsGetResponse struct.
type AppsGetResponse struct {
Count int `json:"count"`
Items []object.AppsApp `json:"items"`
object.ExtendedResponse
}
// AppsGet returns applications data.
//
// https://vk.com/dev/apps.get
func (vk *VK) AppsGet(params Params) (response AppsGetResponse, err error) {
err = vk.RequestUnmarshal("apps.get", &response, params)
return
}
// AppsGetCatalogResponse struct.
type AppsGetCatalogResponse struct {
Count int `json:"count"`
Items []object.AppsApp `json:"items"`
object.ExtendedResponse
}
// AppsGetCatalog returns a list of applications (apps) available to users in the App Catalog.
//
// https://vk.com/dev/apps.getCatalog
func (vk *VK) AppsGetCatalog(params Params) (response AppsGetCatalogResponse, err error) {
err = vk.RequestUnmarshal("apps.getCatalog", &response, params)
return
}
// AppsGetFriendsListResponse struct.
type AppsGetFriendsListResponse struct {
Count int `json:"count"`
Items []int `json:"items"`
}
// AppsGetFriendsList creates friends list for requests and invites in current app.
//
// extended=0
//
// https://vk.com/dev/apps.getFriendsList
func (vk *VK) AppsGetFriendsList(params Params) (response AppsGetFriendsListResponse, err error) {
err = vk.RequestUnmarshal("apps.getFriendsList", &response, params, Params{"extended": false})
return
}
// AppsGetFriendsListExtendedResponse struct.
type AppsGetFriendsListExtendedResponse struct {
Count int `json:"count"`
Items []object.UsersUser `json:"items"`
}
// AppsGetFriendsListExtended creates friends list for requests and invites in current app.
//
// extended=1
//
// https://vk.com/dev/apps.getFriendsList
func (vk *VK) AppsGetFriendsListExtended(params Params) (response AppsGetFriendsListExtendedResponse, err error) {
err = vk.RequestUnmarshal("apps.getFriendsList", &response, params, Params{"extended": true})
return
}
// AppsGetLeaderboardResponse struct.
type AppsGetLeaderboardResponse struct {
Count int `json:"count"`
Items []object.AppsLeaderboard `json:"items"`
}
// AppsGetLeaderboard returns players rating in the game.
//
// extended=0
//
// https://vk.com/dev/apps.getLeaderboard
func (vk *VK) AppsGetLeaderboard(params Params) (response AppsGetLeaderboardResponse, err error) {
err = vk.RequestUnmarshal("apps.getLeaderboard", &response, params, Params{"extended": false})
return
}
// AppsGetLeaderboardExtendedResponse struct.
type AppsGetLeaderboardExtendedResponse struct {
Count int `json:"count"`
Items []struct {
Score int `json:"score"`
UserID int `json:"user_id"`
} `json:"items"`
Profiles []object.UsersUser `json:"profiles"`
}
// AppsGetLeaderboardExtended returns players rating in the game.
//
// extended=1
//
// https://vk.com/dev/apps.getLeaderboard
func (vk *VK) AppsGetLeaderboardExtended(params Params) (response AppsGetLeaderboardExtendedResponse, err error) {
err = vk.RequestUnmarshal("apps.getLeaderboard", &response, params, Params{"extended": true})
return
}
// AppsGetScopesResponse struct.
type AppsGetScopesResponse struct {
Count int `json:"count"`
Items []object.AppsScope `json:"items"`
}
// AppsGetScopes ...
//
// TODO: write docs.
//
// https://vk.com/dev/apps.getScopes
func (vk *VK) AppsGetScopes(params Params) (response AppsGetScopesResponse, err error) {
err = vk.RequestUnmarshal("apps.getScopes", &response, params)
return
}
// AppsGetScore returns user score in app.
//
// NOTE: vk wtf!?
//
// https://vk.com/dev/apps.getScore
func (vk *VK) AppsGetScore(params Params) (response string, err error) {
err = vk.RequestUnmarshal("apps.getScore", &response, params)
return
}
// AppsSendRequest sends a request to another user in an app that uses VK authorization.
//
// https://vk.com/dev/apps.sendRequest
func (vk *VK) AppsSendRequest(params Params) (response int, err error) {
err = vk.RequestUnmarshal("apps.sendRequest", &response, params)
return
}

View File

@@ -0,0 +1,100 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// AppWidgetsGetAppImageUploadServerResponse struct.
type AppWidgetsGetAppImageUploadServerResponse struct {
UploadURL string `json:"upload_url"`
}
// AppWidgetsGetAppImageUploadServer returns a URL for uploading a
// photo to the app collection for community app widgets.
//
// https://vk.com/dev/appWidgets.getAppImageUploadServer
func (vk *VK) AppWidgetsGetAppImageUploadServer(params Params) (
response AppWidgetsGetAppImageUploadServerResponse,
err error,
) {
err = vk.RequestUnmarshal("appWidgets.getAppImageUploadServer", &response, params)
return
}
// AppWidgetsGetAppImagesResponse struct.
type AppWidgetsGetAppImagesResponse struct {
Count int `json:"count"`
Items []object.AppWidgetsImage `json:"items"`
}
// AppWidgetsGetAppImages returns an app collection of images for community app widgets.
//
// https://vk.com/dev/appWidgets.getAppImages
func (vk *VK) AppWidgetsGetAppImages(params Params) (response AppWidgetsGetAppImagesResponse, err error) {
err = vk.RequestUnmarshal("appWidgets.getAppImages", &response, params)
return
}
// AppWidgetsGetGroupImageUploadServerResponse struct.
type AppWidgetsGetGroupImageUploadServerResponse struct {
UploadURL string `json:"upload_url"`
}
// AppWidgetsGetGroupImageUploadServer returns a URL for uploading
// a photo to the community collection for community app widgets.
//
// https://vk.com/dev/appWidgets.getGroupImageUploadServer
func (vk *VK) AppWidgetsGetGroupImageUploadServer(params Params) (
response AppWidgetsGetGroupImageUploadServerResponse,
err error,
) {
err = vk.RequestUnmarshal("appWidgets.getGroupImageUploadServer", &response, params)
return
}
// AppWidgetsGetGroupImagesResponse struct.
type AppWidgetsGetGroupImagesResponse struct {
Count int `json:"count"`
Items []object.AppWidgetsImage `json:"items"`
}
// AppWidgetsGetGroupImages returns a community collection of images for community app widgets.
//
// https://vk.com/dev/appWidgets.getGroupImages
func (vk *VK) AppWidgetsGetGroupImages(params Params) (response AppWidgetsGetGroupImagesResponse, err error) {
err = vk.RequestUnmarshal("appWidgets.getGroupImages", &response, params)
return
}
// AppWidgetsGetImagesByID returns an image for community app widgets by its ID.
//
// https://vk.com/dev/appWidgets.getImagesById
func (vk *VK) AppWidgetsGetImagesByID(params Params) (response object.AppWidgetsImage, err error) {
err = vk.RequestUnmarshal("appWidgets.getImagesById", &response, params)
return
}
// AppWidgetsSaveAppImage allows to save image into app collection for community app widgets.
//
// https://vk.com/dev/appWidgets.saveAppImage
func (vk *VK) AppWidgetsSaveAppImage(params Params) (response object.AppWidgetsImage, err error) {
err = vk.RequestUnmarshal("appWidgets.saveAppImage", &response, params)
return
}
// AppWidgetsSaveGroupImage allows to save image into community collection for community app widgets.
//
// https://vk.com/dev/appWidgets.saveGroupImage
func (vk *VK) AppWidgetsSaveGroupImage(params Params) (response object.AppWidgetsImage, err error) {
err = vk.RequestUnmarshal("appWidgets.saveGroupImage", &response, params)
return
}
// AppWidgetsUpdate allows to update community app widget.
//
// https://vk.com/dev/appWidgets.update
func (vk *VK) AppWidgetsUpdate(params Params) (response int, err error) {
err = vk.RequestUnmarshal("appWidgets.update", &response, params)
return
}

26
vendor/github.com/SevereCloud/vksdk/v2/api/auth.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
// AuthCheckPhone checks a user's phone number for correctness.
//
// https://vk.com/dev/auth.checkPhone
//
// Deprecated: This method is deprecated and may be disabled soon, please avoid
// using it.
func (vk *VK) AuthCheckPhone(params Params) (response int, err error) {
err = vk.RequestUnmarshal("auth.checkPhone", &response, params)
return
}
// AuthRestoreResponse struct.
type AuthRestoreResponse struct {
Success int `json:"success"`
SID string `json:"sid"`
}
// AuthRestore allows to restore account access using a code received via SMS.
//
// https://vk.com/dev/auth.restore
func (vk *VK) AuthRestore(params Params) (response AuthRestoreResponse, err error) {
err = vk.RequestUnmarshal("auth.restore", &response, params)
return
}

173
vendor/github.com/SevereCloud/vksdk/v2/api/board.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// BoardAddTopic creates a new topic on a community's discussion board.
//
// https://vk.com/dev/board.addTopic
func (vk *VK) BoardAddTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.addTopic", &response, params)
return
}
// BoardCloseTopic closes a topic on a community's discussion board so that comments cannot be posted.
//
// https://vk.com/dev/board.closeTopic
func (vk *VK) BoardCloseTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.closeTopic", &response, params)
return
}
// BoardCreateComment adds a comment on a topic on a community's discussion board.
//
// https://vk.com/dev/board.createComment
func (vk *VK) BoardCreateComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.createComment", &response, params)
return
}
// BoardDeleteComment deletes a comment on a topic on a community's discussion board.
//
// https://vk.com/dev/board.deleteComment
func (vk *VK) BoardDeleteComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.deleteComment", &response, params)
return
}
// BoardDeleteTopic deletes a topic from a community's discussion board.
//
// https://vk.com/dev/board.deleteTopic
func (vk *VK) BoardDeleteTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.deleteTopic", &response, params)
return
}
// BoardEditComment edits a comment on a topic on a community's discussion board.
//
// https://vk.com/dev/board.editComment
func (vk *VK) BoardEditComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.editComment", &response, params)
return
}
// BoardEditTopic edits the title of a topic on a community's discussion board.
//
// https://vk.com/dev/board.editTopic
func (vk *VK) BoardEditTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.editTopic", &response, params)
return
}
// BoardFixTopic pins a topic (fixes its place) to the top of a community's discussion board.
//
// https://vk.com/dev/board.fixTopic
func (vk *VK) BoardFixTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.fixTopic", &response, params)
return
}
// BoardGetCommentsResponse struct.
type BoardGetCommentsResponse struct {
Count int `json:"count"`
Items []object.BoardTopicComment `json:"items"`
Poll object.BoardTopicPoll `json:"poll"`
RealOffset int `json:"real_offset"`
}
// BoardGetComments returns a list of comments on a topic on a community's discussion board.
//
// extended=0
//
// https://vk.com/dev/board.getComments
func (vk *VK) BoardGetComments(params Params) (response BoardGetCommentsResponse, err error) {
err = vk.RequestUnmarshal("board.getComments", &response, params, Params{"extended": false})
return
}
// BoardGetCommentsExtendedResponse struct.
type BoardGetCommentsExtendedResponse struct {
Count int `json:"count"`
Items []object.BoardTopicComment `json:"items"`
Poll object.BoardTopicPoll `json:"poll"`
RealOffset int `json:"real_offset"`
Profiles []object.UsersUser `json:"profiles"`
Groups []object.GroupsGroup `json:"groups"`
}
// BoardGetCommentsExtended returns a list of comments on a topic on a community's discussion board.
//
// extended=1
//
// https://vk.com/dev/board.getComments
func (vk *VK) BoardGetCommentsExtended(params Params) (response BoardGetCommentsExtendedResponse, err error) {
err = vk.RequestUnmarshal("board.getComments", &response, params, Params{"extended": true})
return
}
// BoardGetTopicsResponse struct.
type BoardGetTopicsResponse struct {
Count int `json:"count"`
Items []object.BoardTopic `json:"items"`
DefaultOrder float64 `json:"default_order"` // BUG(VK): default_order int https://vk.com/bug136682
CanAddTopics object.BaseBoolInt `json:"can_add_topics"`
}
// BoardGetTopics returns a list of topics on a community's discussion board.
//
// extended=0
//
// https://vk.com/dev/board.getTopics
func (vk *VK) BoardGetTopics(params Params) (response BoardGetTopicsResponse, err error) {
err = vk.RequestUnmarshal("board.getTopics", &response, params, Params{"extended": false})
return
}
// BoardGetTopicsExtendedResponse struct.
type BoardGetTopicsExtendedResponse struct {
Count int `json:"count"`
Items []object.BoardTopic `json:"items"`
DefaultOrder float64 `json:"default_order"` // BUG(VK): default_order int https://vk.com/bug136682
CanAddTopics object.BaseBoolInt `json:"can_add_topics"`
Profiles []object.UsersUser `json:"profiles"`
Groups []object.GroupsGroup `json:"groups"`
}
// BoardGetTopicsExtended returns a list of topics on a community's discussion board.
//
// extended=1
//
// https://vk.com/dev/board.getTopics
func (vk *VK) BoardGetTopicsExtended(params Params) (response BoardGetTopicsExtendedResponse, err error) {
err = vk.RequestUnmarshal("board.getTopics", &response, params, Params{"extended": true})
return
}
// BoardOpenTopic re-opens a previously closed topic on a community's discussion board.
//
// https://vk.com/dev/board.openTopic
func (vk *VK) BoardOpenTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.openTopic", &response, params)
return
}
// BoardRestoreComment restores a comment deleted from a topic on a community's discussion board.
//
// https://vk.com/dev/board.restoreComment
func (vk *VK) BoardRestoreComment(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.restoreComment", &response, params)
return
}
// BoardUnfixTopic unpins a pinned topic from the top of a community's discussion board.
//
// https://vk.com/dev/board.unfixTopic
func (vk *VK) BoardUnfixTopic(params Params) (response int, err error) {
err = vk.RequestUnmarshal("board.unfixTopic", &response, params)
return
}

View File

@@ -0,0 +1,7 @@
package api
// CaptchaForce api method.
func (vk *VK) CaptchaForce(params Params) (response int, err error) {
err = vk.RequestUnmarshal("captcha.force", &response, params)
return
}

163
vendor/github.com/SevereCloud/vksdk/v2/api/database.go generated vendored Normal file
View File

@@ -0,0 +1,163 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// DatabaseGetChairsResponse struct.
type DatabaseGetChairsResponse struct {
Count int `json:"count"`
Items []object.BaseObject `json:"items"`
}
// DatabaseGetChairs returns list of chairs on a specified faculty.
//
// https://vk.com/dev/database.getChairs
func (vk *VK) DatabaseGetChairs(params Params) (response DatabaseGetChairsResponse, err error) {
err = vk.RequestUnmarshal("database.getChairs", &response, params)
return
}
// DatabaseGetCitiesResponse struct.
type DatabaseGetCitiesResponse struct {
Count int `json:"count"`
Items []object.DatabaseCity `json:"items"`
}
// DatabaseGetCities returns a list of cities.
//
// https://vk.com/dev/database.getCities
func (vk *VK) DatabaseGetCities(params Params) (response DatabaseGetCitiesResponse, err error) {
err = vk.RequestUnmarshal("database.getCities", &response, params)
return
}
// DatabaseGetCitiesByIDResponse struct.
type DatabaseGetCitiesByIDResponse []object.DatabaseCity
// DatabaseGetCitiesByID returns information about cities by their IDs.
//
// https://vk.com/dev/database.getCitiesByID
func (vk *VK) DatabaseGetCitiesByID(params Params) (response DatabaseGetCitiesByIDResponse, err error) {
err = vk.RequestUnmarshal("database.getCitiesById", &response, params)
return
}
// DatabaseGetCountriesResponse struct.
type DatabaseGetCountriesResponse struct {
Count int `json:"count"`
Items []object.BaseObject `json:"items"`
}
// DatabaseGetCountries returns a list of countries.
//
// https://vk.com/dev/database.getCountries
func (vk *VK) DatabaseGetCountries(params Params) (response DatabaseGetCountriesResponse, err error) {
err = vk.RequestUnmarshal("database.getCountries", &response, params)
return
}
// DatabaseGetCountriesByIDResponse struct.
type DatabaseGetCountriesByIDResponse []object.BaseObject
// DatabaseGetCountriesByID returns information about countries by their IDs.
//
// https://vk.com/dev/database.getCountriesByID
func (vk *VK) DatabaseGetCountriesByID(params Params) (response DatabaseGetCountriesByIDResponse, err error) {
err = vk.RequestUnmarshal("database.getCountriesById", &response, params)
return
}
// DatabaseGetFacultiesResponse struct.
type DatabaseGetFacultiesResponse struct {
Count int `json:"count"`
Items []object.DatabaseFaculty `json:"items"`
}
// DatabaseGetFaculties returns a list of faculties (i.e., university departments).
//
// https://vk.com/dev/database.getFaculties
func (vk *VK) DatabaseGetFaculties(params Params) (response DatabaseGetFacultiesResponse, err error) {
err = vk.RequestUnmarshal("database.getFaculties", &response, params)
return
}
// DatabaseGetMetroStationsResponse struct.
type DatabaseGetMetroStationsResponse struct {
Count int `json:"count"`
Items []object.DatabaseMetroStation `json:"items"`
}
// DatabaseGetMetroStations returns the list of metro stations.
//
// https://vk.com/dev/database.getMetroStations
func (vk *VK) DatabaseGetMetroStations(params Params) (response DatabaseGetMetroStationsResponse, err error) {
err = vk.RequestUnmarshal("database.getMetroStations", &response, params)
return
}
// DatabaseGetMetroStationsByIDResponse struct.
type DatabaseGetMetroStationsByIDResponse []object.DatabaseMetroStation
// DatabaseGetMetroStationsByID returns information about one or several metro stations by their identifiers.
//
// https://vk.com/dev/database.getMetroStationsById
func (vk *VK) DatabaseGetMetroStationsByID(params Params) (response DatabaseGetMetroStationsByIDResponse, err error) {
err = vk.RequestUnmarshal("database.getMetroStationsById", &response, params)
return
}
// DatabaseGetRegionsResponse struct.
type DatabaseGetRegionsResponse struct {
Count int `json:"count"`
Items []object.DatabaseRegion `json:"items"`
}
// DatabaseGetRegions returns a list of regions.
//
// https://vk.com/dev/database.getRegions
func (vk *VK) DatabaseGetRegions(params Params) (response DatabaseGetRegionsResponse, err error) {
err = vk.RequestUnmarshal("database.getRegions", &response, params)
return
}
// DatabaseGetSchoolClassesResponse struct.
type DatabaseGetSchoolClassesResponse [][]interface{}
// DatabaseGetSchoolClasses returns a list of school classes specified for the country.
//
// BUG(VK): database.getSchoolClasses bad return.
//
// https://vk.com/dev/database.getSchoolClasses
func (vk *VK) DatabaseGetSchoolClasses(params Params) (response DatabaseGetSchoolClassesResponse, err error) {
err = vk.RequestUnmarshal("database.getSchoolClasses", &response, params)
return
}
// DatabaseGetSchoolsResponse struct.
type DatabaseGetSchoolsResponse struct {
Count int `json:"count"`
Items []object.DatabaseSchool `json:"items"`
}
// DatabaseGetSchools returns a list of schools.
//
// https://vk.com/dev/database.getSchools
func (vk *VK) DatabaseGetSchools(params Params) (response DatabaseGetSchoolsResponse, err error) {
err = vk.RequestUnmarshal("database.getSchools", &response, params)
return
}
// DatabaseGetUniversitiesResponse struct.
type DatabaseGetUniversitiesResponse struct {
Count int `json:"count"`
Items []object.DatabaseUniversity `json:"items"`
}
// DatabaseGetUniversities returns a list of higher education institutions.
//
// https://vk.com/dev/database.getUniversities
func (vk *VK) DatabaseGetUniversities(params Params) (response DatabaseGetUniversitiesResponse, err error) {
err = vk.RequestUnmarshal("database.getUniversities", &response, params)
return
}

137
vendor/github.com/SevereCloud/vksdk/v2/api/docs.go generated vendored Normal file
View File

@@ -0,0 +1,137 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// DocsAdd copies a document to a user's or community's document list.
//
// https://vk.com/dev/docs.add
func (vk *VK) DocsAdd(params Params) (response int, err error) {
err = vk.RequestUnmarshal("docs.add", &response, params)
return
}
// DocsDelete deletes a user or community document.
//
// https://vk.com/dev/docs.delete
func (vk *VK) DocsDelete(params Params) (response int, err error) {
err = vk.RequestUnmarshal("docs.delete", &response, params)
return
}
// DocsEdit edits a document.
//
// https://vk.com/dev/docs.edit
func (vk *VK) DocsEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("docs.edit", &response, params)
return
}
// DocsGetResponse struct.
type DocsGetResponse struct {
Count int `json:"count"`
Items []object.DocsDoc `json:"items"`
}
// DocsGet returns detailed information about user or community documents.
//
// https://vk.com/dev/docs.get
func (vk *VK) DocsGet(params Params) (response DocsGetResponse, err error) {
err = vk.RequestUnmarshal("docs.get", &response, params)
return
}
// DocsGetByIDResponse struct.
type DocsGetByIDResponse []object.DocsDoc
// DocsGetByID returns information about documents by their IDs.
//
// https://vk.com/dev/docs.getById
func (vk *VK) DocsGetByID(params Params) (response DocsGetByIDResponse, err error) {
err = vk.RequestUnmarshal("docs.getById", &response, params)
return
}
// DocsGetMessagesUploadServerResponse struct.
type DocsGetMessagesUploadServerResponse struct {
UploadURL string `json:"upload_url"`
}
// DocsGetMessagesUploadServer returns the server address for document upload.
//
// https://vk.com/dev/docs.getMessagesUploadServer
func (vk *VK) DocsGetMessagesUploadServer(params Params) (response DocsGetMessagesUploadServerResponse, err error) {
err = vk.RequestUnmarshal("docs.getMessagesUploadServer", &response, params)
return
}
// DocsGetTypesResponse struct.
type DocsGetTypesResponse struct {
Count int `json:"count"`
Items []object.DocsDocTypes `json:"items"`
}
// DocsGetTypes returns documents types available for current user.
//
// https://vk.com/dev/docs.getTypes
func (vk *VK) DocsGetTypes(params Params) (response DocsGetTypesResponse, err error) {
err = vk.RequestUnmarshal("docs.getTypes", &response, params)
return
}
// DocsGetUploadServerResponse struct.
type DocsGetUploadServerResponse struct {
UploadURL string `json:"upload_url"`
}
// DocsGetUploadServer returns the server address for document upload.
//
// https://vk.com/dev/docs.getUploadServer
func (vk *VK) DocsGetUploadServer(params Params) (response DocsGetUploadServerResponse, err error) {
err = vk.RequestUnmarshal("docs.getUploadServer", &response, params)
return
}
// DocsGetWallUploadServerResponse struct.
type DocsGetWallUploadServerResponse struct {
UploadURL string `json:"upload_url"`
}
// DocsGetWallUploadServer returns the server address for document upload onto a user's or community's wall.
//
// https://vk.com/dev/docs.getWallUploadServer
func (vk *VK) DocsGetWallUploadServer(params Params) (response DocsGetWallUploadServerResponse, err error) {
err = vk.RequestUnmarshal("docs.getWallUploadServer", &response, params)
return
}
// DocsSaveResponse struct.
type DocsSaveResponse struct {
Type string `json:"type"`
AudioMessage object.MessagesAudioMessage `json:"audio_message"`
Doc object.DocsDoc `json:"doc"`
Graffiti object.MessagesGraffiti `json:"graffiti"`
}
// DocsSave saves a document after uploading it to a server.
//
// https://vk.com/dev/docs.save
func (vk *VK) DocsSave(params Params) (response DocsSaveResponse, err error) {
err = vk.RequestUnmarshal("docs.save", &response, params)
return
}
// DocsSearchResponse struct.
type DocsSearchResponse struct {
Count int `json:"count"`
Items []object.DocsDoc `json:"items"`
}
// DocsSearch returns a list of documents matching the search criteria.
//
// https://vk.com/dev/docs.search
func (vk *VK) DocsSearch(params Params) (response DocsSearchResponse, err error) {
err = vk.RequestUnmarshal("docs.search", &response, params)
return
}

49
vendor/github.com/SevereCloud/vksdk/v2/api/donut.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
package api
import "github.com/SevereCloud/vksdk/v2/object"
// DonutGetFriendsResponse struct.
type DonutGetFriendsResponse struct {
Count int `json:"count"`
Items []object.UsersUser `json:"items"`
}
// DonutGetFriends method.
//
// https://vk.com/dev/donut.getFriends
func (vk *VK) DonutGetFriends(params Params) (response DonutGetFriendsResponse, err error) {
err = vk.RequestUnmarshal("donut.getFriends", &response, params)
return
}
// DonutGetSubscription method.
//
// https://vk.com/dev/donut.getSubscription
func (vk *VK) DonutGetSubscription(params Params) (response object.DonutDonatorSubscriptionInfo, err error) {
err = vk.RequestUnmarshal("donut.getSubscription", &response, params)
return
}
// DonutGetSubscriptionsResponse struct.
type DonutGetSubscriptionsResponse struct {
Subscriptions []object.DonutDonatorSubscriptionInfo `json:"subscriptions"`
Count int `json:"count"`
Profiles []object.UsersUser `json:"profiles"`
Groups []object.GroupsGroup `json:"groups"`
}
// DonutGetSubscriptions method.
//
// https://vk.com/dev/donut.getSubscriptions
func (vk *VK) DonutGetSubscriptions(params Params) (response DonutGetSubscriptionsResponse, err error) {
err = vk.RequestUnmarshal("donut.getSubscriptions", &response, params)
return
}
// DonutIsDon method.
//
// https://vk.com/dev/donut.isDon
func (vk *VK) DonutIsDon(params Params) (response int, err error) {
err = vk.RequestUnmarshal("donut.isDon", &response, params)
return
}

View File

@@ -0,0 +1,19 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// DownloadedGamesGetPaidStatusResponse struct.
type DownloadedGamesGetPaidStatusResponse struct {
IsPaid object.BaseBoolInt `json:"is_paid"`
}
// DownloadedGamesGetPaidStatus method.
//
// https://vk.com/dev/downloadedGames.getPaidStatus
func (vk *VK) DownloadedGamesGetPaidStatus(params Params) (response DownloadedGamesGetPaidStatusResponse, err error) {
err = vk.RequestUnmarshal("downloadedGames.getPaidStatus", &response, params, Params{"extended": false})
return
}

927
vendor/github.com/SevereCloud/vksdk/v2/api/errors.go generated vendored Normal file
View File

@@ -0,0 +1,927 @@
package api
import (
"errors"
"fmt"
"github.com/SevereCloud/vksdk/v2/object"
)
// ErrorType is the type of an error.
type ErrorType int
// Error returns the message of a ErrorType.
func (e ErrorType) Error() string {
return fmt.Sprintf("api: error with code %d", e)
}
// Error codes. See https://vk.com/dev/errors
const (
ErrNoType ErrorType = 0 // NoType error
// Unknown error occurred
//
// Try again later.
ErrUnknown ErrorType = 1
// Application is disabled. Enable your application or use test mode
//
// You need to switch on the app in Settings
// https://vk.com/editapp?id={Your API_ID}
// or use the test mode (test_mode=1).
ErrDisabled ErrorType = 2
// Unknown method passed.
//
// Check the method name: http://vk.com/dev/methods
ErrMethod ErrorType = 3
ErrSignature ErrorType = 4 // Incorrect signature
// User authorization failed
//
// Make sure that you use a correct authorization type.
ErrAuth ErrorType = 5
// Too many requests per second
//
// Decrease the request frequency or use the execute method.
// More details on frequency limits here:
// https://vk.com/dev/api_requests
ErrTooMany ErrorType = 6
// Permission to perform this action is denied
//
// Make sure that your have received required permissions during the
// authorization.
// You can do it with the account.getAppPermissions method.
// https://vk.com/dev/permissions
ErrPermission ErrorType = 7
// Invalid request
//
// Check the request syntax and used parameters list (it can be found on
// a method description page).
ErrRequest ErrorType = 8
// Flood control
//
// You need to decrease the count of identical requests. For more efficient
// work you may use execute.
ErrFlood ErrorType = 9
// Internal server error
//
// Try again later.
ErrServer ErrorType = 10
// In test mode application should be disabled or user should be authorized
//
// Switch the app off in Settings:
//
// https://vk.com/editapp?id={Your API_ID}
//
ErrEnabledInTest ErrorType = 11
// Unable to compile code.
ErrCompile ErrorType = 12
// Runtime error occurred during code invocation.
ErrRuntime ErrorType = 13
// Captcha needed.
//
// See https://vk.com/dev/captcha_error
ErrCaptcha ErrorType = 14
// Access denied
//
// Make sure that you use correct identifiers and the content is available
// for the user in the full version of the site.
ErrAccess ErrorType = 15
// HTTP authorization failed
//
// To avoid this error check if a user has the 'Use secure connection'
// option enabled with the account.getInfo method.
ErrAuthHTTPS ErrorType = 16
// Validation required
//
// Make sure that you don't use a token received with
// http://vk.com/dev/auth_mobile for a request from the server.
// It's restricted.
//
// https://vk.com/dev/need_validation
ErrAuthValidation ErrorType = 17
ErrUserDeleted ErrorType = 18 // User was deleted or banned
ErrBlocked ErrorType = 19 // Content blocked
// Permission to perform this action is denied for non-standalone
// applications.
ErrMethodPermission ErrorType = 20
// Permission to perform this action is allowed only for standalone and
// OpenAPI applications.
ErrMethodAds ErrorType = 21
ErrUpload ErrorType = 22 // Upload error
// This method was disabled.
//
// All the methods available now are listed here: http://vk.com/dev/methods
ErrMethodDisabled ErrorType = 23
// Confirmation required
//
// In some cases VK requires to request action confirmation from the user
// (for Standalone apps only).
//
// Following parameter is transmitted with the error message as well:
//
// confirmation_text text of the message to be shown in the default
// confirmation window.
//
// The app should display the default confirmation window
// with text from confirmation_text and two buttons: "Continue" and
// "Cancel".
// If user confirms the action repeat the request with an extra parameter:
//
// confirm = 1.
//
// https://vk.com/dev/need_confirmation
ErrNeedConfirmation ErrorType = 24
ErrNeedTokenConfirmation ErrorType = 25 // Token confirmation required
ErrGroupAuth ErrorType = 27 // Group authorization failed
ErrAppAuth ErrorType = 28 // Application authorization failed
// Rate limit reached.
//
// More details on rate limits here: https://vk.com/dev/data_limits
ErrRateLimit ErrorType = 29
ErrPrivateProfile ErrorType = 30 // This profile is private
// Method execution was interrupted due to timeout.
ErrExecutionTimeout ErrorType = 36
// User was banned.
ErrUserBanned ErrorType = 37
// Unknown application.
ErrUnknownApplication ErrorType = 38
// Unknown user.
ErrUnknownUser ErrorType = 39
// Unknown group.
ErrUnknownGroup ErrorType = 40
// One of the parameters specified was missing or invalid
//
// Check the required parameters list and their format on a method
// description page.
ErrParam ErrorType = 100
// Invalid application API ID
//
// Find the app in the administrated list in settings:
// http://vk.com/apps?act=settings
// And set the correct API_ID in the request.
ErrParamAPIID ErrorType = 101
ErrLimits ErrorType = 103 // Out of limits
ErrNotFound ErrorType = 104 // Not found
ErrSaveFile ErrorType = 105 // Couldn't save file
ErrActionFailed ErrorType = 106 // Unable to process action
// Invalid user id
//
// Make sure that you use a correct id. You can get an id using a screen
// name with the utils.resolveScreenName method.
ErrParamUserID ErrorType = 113
ErrParamAlbumID ErrorType = 114 // Invalid album id
ErrParamServer ErrorType = 118 // Invalid server
ErrParamTitle ErrorType = 119 // Invalid title
ErrParamPhotos ErrorType = 122 // Invalid photos
ErrParamHash ErrorType = 121 // Invalid hash
ErrParamPhoto ErrorType = 129 // Invalid photo
ErrParamGroupID ErrorType = 125 // Invalid group id
ErrParamPageID ErrorType = 140 // Page not found
ErrAccessPage ErrorType = 141 // Access to page denied
// The mobile number of the user is unknown.
ErrMobileNotActivated ErrorType = 146
// Application has insufficient funds.
ErrInsufficientFunds ErrorType = 147
// Access to the menu of the user denied.
ErrAccessMenu ErrorType = 148
// Invalid timestamp
//
// You may get a correct value with the utils.getServerTime method.
ErrParamTimestamp ErrorType = 150
ErrFriendsListID ErrorType = 171 // Invalid list id
// Reached the maximum number of lists.
ErrFriendsListLimit ErrorType = 173
// Cannot add user himself as friend.
ErrFriendsAddYourself ErrorType = 174
// Cannot add this user to friends as they have put you on their blacklist.
ErrFriendsAddInEnemy ErrorType = 175
// Cannot add this user to friends as you put him on blacklist.
ErrFriendsAddEnemy ErrorType = 176
// Cannot add this user to friends as user not found.
ErrFriendsAddNotFound ErrorType = 177
ErrParamNoteID ErrorType = 180 // Note not found
ErrAccessNote ErrorType = 181 // Access to note denied
ErrAccessNoteComment ErrorType = 182 // You can't comment this note
ErrAccessComment ErrorType = 183 // Access to comment denied
// Access to album denied
//
// Make sure you use correct ids (owner_id is always positive for users,
// negative for communities) and the current user has access to the
// requested content in the full version of the site.
ErrAccessAlbum ErrorType = 200
// Access to audio denied
//
// Make sure you use correct ids (owner_id is always positive for users,
// negative for communities) and the current user has access to the
// requested content in the full version of the site.
ErrAccessAudio ErrorType = 201
// Access to group denied
//
// Make sure that the current user is a member or admin of the community
// (for closed and private groups and events).
ErrAccessGroup ErrorType = 203
// Access denied.
ErrAccessVideo ErrorType = 204
// Access denied.
ErrAccessMarket ErrorType = 205
// Access to wall's post denied.
ErrWallAccessPost ErrorType = 210
// Access to wall's comment denied.
ErrWallAccessComment ErrorType = 211
// Access to post comments denied.
ErrWallAccessReplies ErrorType = 212
// Access to status replies denied.
ErrWallAccessAddReply ErrorType = 213
// Access to adding post denied.
ErrWallAddPost ErrorType = 214
// Advertisement post was recently added.
ErrWallAdsPublished ErrorType = 219
// Too many recipients.
ErrWallTooManyRecipients ErrorType = 220
// User disabled track name broadcast.
ErrStatusNoAudio ErrorType = 221
// Hyperlinks are forbidden.
ErrWallLinksForbidden ErrorType = 222
// Too many replies.
ErrWallReplyOwnerFlood ErrorType = 223
// Too many ads posts.
ErrWallAdsPostLimitReached ErrorType = 224
// Donut is disabled.
ErrDonutDisabled ErrorType = 225
// Reaction can not be applied to the object.
ErrLikesReactionCanNotBeApplied ErrorType = 232
// Access to poll denied.
ErrPollsAccess ErrorType = 250
// Invalid answer id.
ErrPollsAnswerID ErrorType = 252
// Invalid poll id.
ErrPollsPollID ErrorType = 251
// Access denied, please vote first.
ErrPollsAccessWithoutVote ErrorType = 253
// Access to the groups list is denied due to the user's privacy settings.
ErrAccessGroups ErrorType = 260
// This album is full
//
// You need to delete the odd objects from the album or use another album.
ErrAlbumFull ErrorType = 300
ErrAlbumsLimit ErrorType = 302 // Albums number limit is reached
// Permission denied. You must enable votes processing in application
// settings
//
// Check the app settings:
//
// http://vk.com/editapp?id={Your API_ID}&section=payments
//
ErrVotesPermission ErrorType = 500
// Not enough votes.
ErrVotes ErrorType = 503
// Not enough money on owner's balance.
ErrNotEnoughMoney ErrorType = 504
// Permission denied. You have no access to operations specified with
// given object(s).
ErrAdsPermission ErrorType = 600
// Permission denied. You have requested too many actions this day. Try
// later.
ErrWeightedFlood ErrorType = 601
// Some part of the request has not been completed.
ErrAdsPartialSuccess ErrorType = 602
// Some ads error occurred.
ErrAdsSpecific ErrorType = 603
// Invalid domain.
ErrAdsDomainInvalid ErrorType = 604
// Domain is forbidden.
ErrAdsDomainForbidden ErrorType = 605
// Domain is reserved.
ErrAdsDomainReserved ErrorType = 606
// Domain is occupied.
ErrAdsDomainOccupied ErrorType = 607
// Domain is active.
ErrAdsDomainActive ErrorType = 608
// Domain app is invalid.
ErrAdsDomainAppInvalid ErrorType = 609
// Domain app is forbidden.
ErrAdsDomainAppForbidden ErrorType = 610
// Application must be verified.
ErrAdsApplicationMustBeVerified ErrorType = 611
// Application must be in domains list of site of ad unit.
ErrAdsApplicationMustBeInDomainsList ErrorType = 612
// Application is blocked.
ErrAdsApplicationBlocked ErrorType = 613
// Domain of type specified is forbidden in current office type.
ErrAdsDomainTypeForbiddenInCurrentOffice ErrorType = 614
// Domain group is invalid.
ErrAdsDomainGroupInvalid ErrorType = 615
// Domain group is forbidden.
ErrAdsDomainGroupForbidden ErrorType = 616
// Domain app is blocked.
ErrAdsDomainAppBlocked ErrorType = 617
// Domain group is not open.
ErrAdsDomainGroupNotOpen ErrorType = 618
// Domain group is not possible to be joined to adsweb.
ErrAdsDomainGroupNotPossibleJoined ErrorType = 619
// Domain group is blocked.
ErrAdsDomainGroupBlocked ErrorType = 620
// Domain group has restriction: links are forbidden.
ErrAdsDomainGroupLinksForbidden ErrorType = 621
// Domain group has restriction: excluded from search.
ErrAdsDomainGroupExcludedFromSearch ErrorType = 622
// Domain group has restriction: cover is forbidden.
ErrAdsDomainGroupCoverForbidden ErrorType = 623
// Domain group has wrong category.
ErrAdsDomainGroupWrongCategory ErrorType = 624
// Domain group has wrong name.
ErrAdsDomainGroupWrongName ErrorType = 625
// Domain group has low posts reach.
ErrAdsDomainGroupLowPostsReach ErrorType = 626
// Domain group has wrong class.
ErrAdsDomainGroupWrongClass ErrorType = 627
// Domain group is created recently.
ErrAdsDomainGroupCreatedRecently ErrorType = 628
// Object deleted.
ErrAdsObjectDeleted ErrorType = 629
// Lookalike request with same source already in progress.
ErrAdsLookalikeRequestAlreadyInProgress ErrorType = 630
// Max count of lookalike requests per day reached.
ErrAdsLookalikeRequestsLimit ErrorType = 631
// Given audience is too small.
ErrAdsAudienceTooSmall ErrorType = 632
// Given audience is too large.
ErrAdsAudienceTooLarge ErrorType = 633
// Lookalike request audience save already in progress.
ErrAdsLookalikeAudienceSaveAlreadyInProgress ErrorType = 634
// Max count of lookalike request audience saves per day reached.
ErrAdsLookalikeSavesLimit ErrorType = 635
// Max count of retargeting groups reached.
ErrAdsRetargetingGroupsLimit ErrorType = 636
// Domain group has active nemesis punishment.
ErrAdsDomainGroupActiveNemesisPunishment ErrorType = 637
// Cannot edit creator role.
ErrGroupChangeCreator ErrorType = 700
// User should be in club.
ErrGroupNotInClub ErrorType = 701
// Too many officers in club.
ErrGroupTooManyOfficers ErrorType = 702
// You need to enable 2FA for this action.
ErrGroupNeed2fa ErrorType = 703
// User needs to enable 2FA for this action.
ErrGroupHostNeed2fa ErrorType = 704
// Too many addresses in club.
ErrGroupTooManyAddresses ErrorType = 706
// "Application is not installed in community.
ErrGroupAppIsNotInstalledInCommunity ErrorType = 711
// Invite link is invalid - expired, deleted or not exists.
ErrGroupInvalidInviteLink ErrorType = 714
// This video is already added.
ErrVideoAlreadyAdded ErrorType = 800
// Comments for this video are closed.
ErrVideoCommentsClosed ErrorType = 801
// Can't send messages for users from blacklist.
ErrMessagesUserBlocked ErrorType = 900
// Can't send messages for users without permission.
ErrMessagesDenySend ErrorType = 901
// Can't send messages to this user due to their privacy settings.
ErrMessagesPrivacy ErrorType = 902
// Value of ts or pts is too old.
ErrMessagesTooOldPts ErrorType = 907
// Value of ts or pts is too new.
ErrMessagesTooNewPts ErrorType = 908
// Can't edit this message, because it's too old.
ErrMessagesEditExpired ErrorType = 909
// Can't sent this message, because it's too big.
ErrMessagesTooBig ErrorType = 910
// Keyboard format is invalid.
ErrMessagesKeyboardInvalid ErrorType = 911
// This is a chat bot feature, change this status in settings.
ErrMessagesChatBotFeature ErrorType = 912
// Too many forwarded messages.
ErrMessagesTooLongForwards ErrorType = 913
// Message is too long.
ErrMessagesTooLongMessage ErrorType = 914
// You don't have access to this chat.
ErrMessagesChatUserNoAccess ErrorType = 917
// You can't see invite link for this chat.
ErrMessagesCantSeeInviteLink ErrorType = 919
// Can't edit this kind of message.
ErrMessagesEditKindDisallowed ErrorType = 920
// Can't forward these messages.
ErrMessagesCantFwd ErrorType = 921
// Can't delete this message for everybody.
ErrMessagesCantDeleteForAll ErrorType = 924
// You are not admin of this chat.
ErrMessagesChatNotAdmin ErrorType = 925
// Chat does not exist.
ErrMessagesChatNotExist ErrorType = 927
// You can't change invite link for this chat.
ErrMessagesCantChangeInviteLink ErrorType = 931
// Your community can't interact with this peer.
ErrMessagesGroupPeerAccess ErrorType = 932
// User not found in chat.
ErrMessagesChatUserNotInChat ErrorType = 935
// Contact not found.
ErrMessagesContactNotFound ErrorType = 936
// Message request already send.
ErrMessagesMessageRequestAlreadySend ErrorType = 939
// Too many posts in messages.
ErrMessagesTooManyPosts ErrorType = 940
// Cannot pin one-time story.
ErrMessagesCantPinOneTimeStory ErrorType = 942
// Cannot use this intent.
ErrMessagesCantUseIntent ErrorType = 943
// Limits overflow for this intent.
ErrMessagesLimitIntent ErrorType = 944
// Chat was disabled.
ErrMessagesChatDisabled ErrorType = 945
// Chat not support.
ErrMessagesChatNotSupported ErrorType = 946
// Can't add user to chat, because user has no access to group.
ErrMessagesMemberAccessToGroupDenied ErrorType = 947
// Can't edit pinned message yet.
ErrMessagesEditPinned ErrorType = 949
// Can't send message, reply timed out.
ErrMessagesReplyTimedOut ErrorType = 950
// Invalid phone number.
ErrParamPhone ErrorType = 1000
// This phone number is used by another user.
ErrPhoneAlreadyUsed ErrorType = 1004
// Too many auth attempts, try again later.
ErrAuthFloodError ErrorType = 1105
// Processing.. Try later.
ErrAuthDelay ErrorType = 1112
// Invalid document id.
ErrParamDocID ErrorType = 1150
// Access to document deleting is denied.
ErrParamDocDeleteAccess ErrorType = 1151
// Invalid document title.
ErrParamDocTitle ErrorType = 1152
// Access to document is denied.
ErrParamDocAccess ErrorType = 1153
// Original photo was changed.
ErrPhotoChanged ErrorType = 1160
// Too many feed lists.
ErrTooManyLists ErrorType = 1170
// This achievement is already unlocked.
ErrAppsAlreadyUnlocked ErrorType = 1251
// Subscription not found.
ErrAppsSubscriptionNotFound ErrorType = 1256
// Subscription is in invalid status.
ErrAppsSubscriptionInvalidStatus ErrorType = 1257
// Invalid screen name.
ErrInvalidAddress ErrorType = 1260
// Catalog is not available for this user.
ErrCommunitiesCatalogDisabled ErrorType = 1310
// Catalog categories are not available for this user.
ErrCommunitiesCategoriesDisabled ErrorType = 1311
// Too late for restore.
ErrMarketRestoreTooLate ErrorType = 1400
// Comments for this market are closed.
ErrMarketCommentsClosed ErrorType = 1401
// Album not found.
ErrMarketAlbumNotFound ErrorType = 1402
// Item not found.
ErrMarketItemNotFound ErrorType = 1403
// Item already added to album.
ErrMarketItemAlreadyAdded ErrorType = 1404
// Too many items.
ErrMarketTooManyItems ErrorType = 1405
// Too many items in album.
ErrMarketTooManyItemsInAlbum ErrorType = 1406
// Too many albums.
ErrMarketTooManyAlbums ErrorType = 1407
// Item has bad links in description.
ErrMarketItemHasBadLinks ErrorType = 1408
// Shop not enabled.
ErrMarketShopNotEnabled ErrorType = 1409
// Variant not found.
ErrMarketVariantNotFound ErrorType = 1416
// Property not found.
ErrMarketPropertyNotFound ErrorType = 1417
// Grouping must have two or more items.
ErrMarketGroupingMustContainMoreThanOneItem ErrorType = 1425
// Item must have distinct properties.
ErrMarketGroupingItemsMustHaveDistinctProperties ErrorType = 1426
// Cart is empty.
ErrMarketOrdersNoCartItems ErrorType = 1427
// Specify width, length, height and weight all together.
ErrMarketInvalidDimensions ErrorType = 1429
// VK Pay status can not be changed.
ErrMarketCantChangeVkpayStatus ErrorType = 1430
// Market was already enabled in this group.
ErrMarketShopAlreadyEnabled ErrorType = 1431
// Market was already disabled in this group.
ErrMarketShopAlreadyDisabled ErrorType = 1432
// Invalid image crop format.
ErrMarketPhotosCropInvalidFormat ErrorType = 1433
// Crop bottom right corner is outside of the image.
ErrMarketPhotosCropOverflow ErrorType = 1434
// Crop size is less than the minimum.
ErrMarketPhotosCropSizeTooLow ErrorType = 1435
// Cart is empty.
ErrMarketCartEmpty ErrorType = 1427
// Specify width, length, height and weight all together.
ErrMarketSpecifyDimensions ErrorType = 1429
// VK Pay status can not be changed.
ErrVKPayStatus ErrorType = 1430
// Market was already enabled in this group.
ErrMarketAlreadyEnabled ErrorType = 1431
// Market was already disabled in this group.
ErrMarketAlreadyDisabled ErrorType = 1432
// Story has already expired.
ErrStoryExpired ErrorType = 1600
// Incorrect reply privacy.
ErrStoryIncorrectReplyPrivacy ErrorType = 1602
// Card not found.
ErrPrettyCardsCardNotFound ErrorType = 1900
// Too many cards.
ErrPrettyCardsTooManyCards ErrorType = 1901
// Card is connected to post.
ErrPrettyCardsCardIsConnectedToPost ErrorType = 1902
// Servers number limit is reached.
ErrCallbackServersLimit ErrorType = 2000
// Specified link is incorrect (can't find source).
ErrWallCheckLinkCantDetermineSource ErrorType = 3102
// Recaptcha needed.
ErrRecaptcha ErrorType = 3300
// Phone validation needed.
ErrPhoneValidation ErrorType = 3301
// Password validation needed.
ErrPasswordValidation ErrorType = 3302
// Otp app validation needed.
ErrOtpAppValidation ErrorType = 3303
// Email confirmation needed.
ErrEmailConfirmation ErrorType = 3304
// Assert votes.
ErrAssertVotes ErrorType = 3305
// Token extension required.
ErrTokenExtension ErrorType = 3609
// User is deactivated.
ErrUserDeactivated ErrorType = 3610
// Service is deactivated for user.
ErrServiceDeactivated ErrorType = 3611
// Can't set AliExpress tag to this type of object.
ErrAliExpressTag ErrorType = 3800
)
// ErrorSubtype is the subtype of an error.
type ErrorSubtype int
// Error returns the message of a ErrorSubtype.
func (e ErrorSubtype) Error() string {
return fmt.Sprintf("api: error with subcode %d", e)
}
// Error struct VK.
type Error struct {
Code ErrorType `json:"error_code"`
Subcode ErrorSubtype `json:"error_subcode"`
Message string `json:"error_msg"`
Text string `json:"error_text"`
CaptchaSID string `json:"captcha_sid"`
CaptchaImg string `json:"captcha_img"`
// In some cases VK requires to request action confirmation from the user
// (for Standalone apps only). Following error will be returned:
//
// Error code: 24
// Error text: Confirmation required
//
// Following parameter is transmitted with the error message as well:
//
// confirmation_text text of the message to be shown in the default
// confirmation window.
//
// The app should display the default confirmation window with text from
// confirmation_text and two buttons: "Continue" and "Cancel". If user
// confirms the action repeat the request with an extra parameter:
// confirm = 1.
//
// See https://vk.com/dev/need_confirmation
ConfirmationText string `json:"confirmation_text"`
// In some cases VK requires a user validation procedure. . As a result
// starting from API version 5.0 (for the older versions captcha_error
// will be requested) following error will be returned as a reply to any
// API request:
//
// Error code: 17
// Error text: Validation Required
//
// Following parameter is transmitted with an error message:
// redirect_uri a special address to open in a browser to pass the
// validation procedure.
//
// After passing the validation a user will be redirected to the service
// page:
//
// https://oauth.vk.com/blank.html#{Data required for validation}
//
// In case of successful validation following parameters will be
// transmitted after #:
//
// https://oauth.vk.com/blank.html#success=1&access_token={NEW USER TOKEN}&user_id={USER ID}
//
// If a token was not received by https a new secret will be transmitted
// as well.
//
// In case of unsuccessful validation following address is transmitted:
//
// https://oauth.vk.com/blank.html#fail=1
//
// See https://vk.com/dev/need_validation
RedirectURI string `json:"redirect_uri"`
RequestParams []object.BaseRequestParam `json:"request_params"`
}
// Error returns the message of a Error.
func (e Error) Error() string {
return "api: " + e.Message
}
// Is unwraps its first argument sequentially looking for an error that matches
// the second.
func (e Error) Is(target error) bool {
var tError *Error
if errors.As(target, &tError) {
return e.Code == tError.Code && e.Message == tError.Message
}
var tErrorType ErrorType
if errors.As(target, &tErrorType) {
return e.Code == tErrorType
}
return false
}
// ExecuteError struct.
//
// TODO: v3 Code is ErrorType.
type ExecuteError struct {
Method string `json:"method"`
Code int `json:"error_code"`
Msg string `json:"error_msg"`
}
// ExecuteErrors type.
type ExecuteErrors []ExecuteError
// Error returns the message of a ExecuteErrors.
func (e ExecuteErrors) Error() string {
return fmt.Sprintf("api: execute errors (%d)", len(e))
}
// InvalidContentType type.
type InvalidContentType struct {
ContentType string
}
// Error returns the message of a InvalidContentType.
func (e InvalidContentType) Error() string {
return "api: invalid content-type"
}
// UploadError type.
type UploadError struct {
Err string `json:"error"`
Code int `json:"error_code"`
Descr string `json:"error_descr"`
IsLogged bool `json:"error_is_logged"`
}
// Error returns the message of a UploadError.
func (e UploadError) Error() string {
if e.Err != "" {
return "api: " + e.Err
}
return fmt.Sprintf("api: upload code %d", e.Code)
}
// AdsError struct.
type AdsError struct {
Code ErrorType `json:"error_code"`
Desc string `json:"error_desc"`
}
// Error returns the message of a AdsError.
func (e AdsError) Error() string {
return "api: " + e.Desc
}
// Is unwraps its first argument sequentially looking for an error that matches
// the second.
func (e AdsError) Is(target error) bool {
var tAdsError *AdsError
if errors.As(target, &tAdsError) {
return e.Code == tAdsError.Code && e.Desc == tAdsError.Desc
}
var tErrorType ErrorType
if errors.As(target, &tErrorType) {
return e.Code == tErrorType
}
return false
}

52
vendor/github.com/SevereCloud/vksdk/v2/api/execute.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package api
import "encoding/json"
// ExecuteWithArgs a universal method for calling a sequence of other methods
// while saving and filtering interim results.
//
// The Args map variable allows you to retrieve the parameters passed during
// the request and avoids code formatting.
//
// return Args.code; // return parameter "code"
// return Args.v; // return parameter "v"
//
// https://vk.com/dev/execute
func (vk *VK) ExecuteWithArgs(code string, params Params, obj interface{}) error {
token := vk.getToken()
reqParams := Params{
"code": code,
"access_token": token,
"v": vk.Version,
}
resp, err := vk.Handler("execute", params, reqParams)
jsonErr := json.Unmarshal(resp.Response, &obj)
if jsonErr != nil {
return jsonErr
}
if resp.ExecuteErrors != nil {
return &resp.ExecuteErrors
}
return err
}
// Execute a universal method for calling a sequence of other methods while
// saving and filtering interim results.
//
// https://vk.com/dev/execute
func (vk *VK) Execute(code string, obj interface{}) error {
return vk.ExecuteWithArgs(code, Params{}, obj)
}
func fmtBool(value bool) string {
if value {
return "1"
}
return "0"
}

231
vendor/github.com/SevereCloud/vksdk/v2/api/fave.go generated vendored Normal file
View File

@@ -0,0 +1,231 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// FaveAddArticle adds a link to user faves.
//
// https://vk.com/dev/fave.addArticle
func (vk *VK) FaveAddArticle(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addArticle", &response, params)
return
}
// FaveAddLink adds a link to user faves.
//
// https://vk.com/dev/fave.addLink
func (vk *VK) FaveAddLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addLink", &response, params)
return
}
// FaveAddPage method.
//
// https://vk.com/dev/fave.addPage
func (vk *VK) FaveAddPage(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addPage", &response, params)
return
}
// FaveAddPost method.
//
// https://vk.com/dev/fave.addPost
func (vk *VK) FaveAddPost(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addPost", &response, params)
return
}
// FaveAddProduct method.
//
// https://vk.com/dev/fave.addProduct
func (vk *VK) FaveAddProduct(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addProduct", &response, params)
return
}
// FaveAddTagResponse struct.
type FaveAddTagResponse object.FaveTag
// FaveAddTag method.
//
// https://vk.com/dev/fave.addTag
func (vk *VK) FaveAddTag(params Params) (response FaveAddTagResponse, err error) {
err = vk.RequestUnmarshal("fave.addTag", &response, params)
return
}
// FaveAddVideo method.
//
// https://vk.com/dev/fave.addVideo
func (vk *VK) FaveAddVideo(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.addVideo", &response, params)
return
}
// FaveEditTag method.
//
// https://vk.com/dev/fave.editTag
func (vk *VK) FaveEditTag(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.editTag", &response, params)
return
}
// FaveGetResponse struct.
type FaveGetResponse struct {
Count int `json:"count"`
Items []object.FaveItem `json:"items"`
}
// FaveGet method.
//
// extended=0
//
// https://vk.com/dev/fave.get
func (vk *VK) FaveGet(params Params) (response FaveGetResponse, err error) {
err = vk.RequestUnmarshal("fave.get", &response, params, Params{"extended": false})
return
}
// FaveGetExtendedResponse struct.
type FaveGetExtendedResponse struct {
Count int `json:"count"`
Items []object.FaveItem `json:"items"`
object.ExtendedResponse
}
// FaveGetExtended method.
//
// extended=1
//
// https://vk.com/dev/fave.get
func (vk *VK) FaveGetExtended(params Params) (response FaveGetExtendedResponse, err error) {
err = vk.RequestUnmarshal("fave.get", &response, params, Params{"extended": true})
return
}
// FaveGetPagesResponse struct.
type FaveGetPagesResponse struct {
Count int `json:"count"`
Items []object.FavePage `json:"items"`
}
// FaveGetPages method.
//
// https://vk.com/dev/fave.getPages
func (vk *VK) FaveGetPages(params Params) (response FaveGetPagesResponse, err error) {
err = vk.RequestUnmarshal("fave.getPages", &response, params)
return
}
// FaveGetTagsResponse struct.
type FaveGetTagsResponse struct {
Count int `json:"count"`
Items []object.FaveTag `json:"items"`
}
// FaveGetTags method.
//
// https://vk.com/dev/fave.getTags
func (vk *VK) FaveGetTags(params Params) (response FaveGetTagsResponse, err error) {
err = vk.RequestUnmarshal("fave.getTags", &response, params)
return
}
// FaveMarkSeen method.
//
// https://vk.com/dev/fave.markSeen
func (vk *VK) FaveMarkSeen(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.markSeen", &response, params)
return
}
// FaveRemoveArticle method.
//
// https://vk.com/dev/fave.removeArticle
func (vk *VK) FaveRemoveArticle(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeArticle", &response, params)
return
}
// FaveRemoveLink removes link from the user's faves.
//
// https://vk.com/dev/fave.removeLink
func (vk *VK) FaveRemoveLink(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeLink", &response, params)
return
}
// FaveRemovePage method.
//
// https://vk.com/dev/fave.removePage
func (vk *VK) FaveRemovePage(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removePage", &response, params)
return
}
// FaveRemovePost method.
//
// https://vk.com/dev/fave.removePost
func (vk *VK) FaveRemovePost(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removePost", &response, params)
return
}
// FaveRemoveProduct method.
//
// https://vk.com/dev/fave.removeProduct
func (vk *VK) FaveRemoveProduct(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeProduct", &response, params)
return
}
// FaveRemoveTag method.
//
// https://vk.com/dev/fave.removeTag
func (vk *VK) FaveRemoveTag(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeTag", &response, params)
return
}
// FaveRemoveVideo method.
//
// https://vk.com/dev/fave.removeVideo
func (vk *VK) FaveRemoveVideo(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.removeVideo", &response, params)
return
}
// FaveReorderTags method.
//
// https://vk.com/dev/fave.reorderTags
func (vk *VK) FaveReorderTags(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.reorderTags", &response, params)
return
}
// FaveSetPageTags method.
//
// https://vk.com/dev/fave.setPageTags
func (vk *VK) FaveSetPageTags(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.setPageTags", &response, params)
return
}
// FaveSetTags method.
//
// https://vk.com/dev/fave.setTags
func (vk *VK) FaveSetTags(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.setTags", &response, params)
return
}
// FaveTrackPageInteraction method.
//
// https://vk.com/dev/fave.trackPageInteraction
func (vk *VK) FaveTrackPageInteraction(params Params) (response int, err error) {
err = vk.RequestUnmarshal("fave.trackPageInteraction", &response, params)
return
}

295
vendor/github.com/SevereCloud/vksdk/v2/api/friends.go generated vendored Normal file
View File

@@ -0,0 +1,295 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import (
"github.com/SevereCloud/vksdk/v2/object"
)
// FriendsAdd approves or creates a friend request.
//
// https://vk.com/dev/friends.add
func (vk *VK) FriendsAdd(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.add", &response, params)
return
}
// FriendsAddListResponse struct.
type FriendsAddListResponse struct {
ListID int `json:"list_id"`
}
// FriendsAddList creates a new friend list for the current user.
//
// https://vk.com/dev/friends.addList
func (vk *VK) FriendsAddList(params Params) (response FriendsAddListResponse, err error) {
err = vk.RequestUnmarshal("friends.addList", &response, params)
return
}
// FriendsAreFriendsResponse struct.
type FriendsAreFriendsResponse []object.FriendsFriendStatus
// FriendsAreFriends checks the current user's friendship status with other specified users.
//
// https://vk.com/dev/friends.areFriends
func (vk *VK) FriendsAreFriends(params Params) (response FriendsAreFriendsResponse, err error) {
err = vk.RequestUnmarshal("friends.areFriends", &response, params)
return
}
// FriendsDeleteResponse struct.
type FriendsDeleteResponse struct {
Success object.BaseBoolInt `json:"success"`
FriendDeleted object.BaseBoolInt `json:"friend_deleted"`
OutRequestDeleted object.BaseBoolInt `json:"out_request_deleted"`
InRequestDeleted object.BaseBoolInt `json:"in_request_deleted"`
SuggestionDeleted object.BaseBoolInt `json:"suggestion_deleted"`
}
// FriendsDelete declines a friend request or deletes a user from the current user's friend list.
//
// https://vk.com/dev/friends.delete
func (vk *VK) FriendsDelete(params Params) (response FriendsDeleteResponse, err error) {
err = vk.RequestUnmarshal("friends.delete", &response, params)
return
}
// FriendsDeleteAllRequests marks all incoming friend requests as viewed.
//
// https://vk.com/dev/friends.deleteAllRequests
func (vk *VK) FriendsDeleteAllRequests(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.deleteAllRequests", &response, params)
return
}
// FriendsDeleteList deletes a friend list of the current user.
//
// https://vk.com/dev/friends.deleteList
func (vk *VK) FriendsDeleteList(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.deleteList", &response, params)
return
}
// FriendsEdit edits the friend lists of the selected user.
//
// https://vk.com/dev/friends.edit
func (vk *VK) FriendsEdit(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.edit", &response, params)
return
}
// FriendsEditList edits a friend list of the current user.
//
// https://vk.com/dev/friends.editList
func (vk *VK) FriendsEditList(params Params) (response int, err error) {
err = vk.RequestUnmarshal("friends.editList", &response, params)
return
}
// FriendsGetResponse struct.
type FriendsGetResponse struct {
Count int `json:"count"`
Items []int `json:"items"`
}
// FriendsGet returns a list of user IDs or detailed information about a user's friends.
//
// https://vk.com/dev/friends.get
func (vk *VK) FriendsGet(params Params) (response FriendsGetResponse, err error) {
err = vk.RequestUnmarshal("friends.get", &response, params)
return
}
// FriendsGetFieldsResponse struct.
type FriendsGetFieldsResponse struct {
Count int `json:"count"`
Items []object.FriendsUserXtrLists `json:"items"`
}
// FriendsGetFields returns a list of user IDs or detailed information about a user's friends.
//
// https://vk.com/dev/friends.get
func (vk *VK) FriendsGetFields(params Params) (response FriendsGetFieldsResponse, err error) {
reqParams := make(Params)
if v, prs := params["fields"]; v == "" || !prs {
reqParams["fields"] = "id"
}
err = vk.RequestUnmarshal("friends.get", &response, params, reqParams)
return
}
// FriendsGetAppUsersResponse struct.
type FriendsGetAppUsersResponse []int
// FriendsGetAppUsers returns a list of IDs of the current user's friends who installed the application.
//
// https://vk.com/dev/friends.getAppUsers
func (vk *VK) FriendsGetAppUsers(params Params) (response FriendsGetAppUsersResponse, err error) {
err = vk.RequestUnmarshal("friends.getAppUsers", &response, params)
return
}
// FriendsGetByPhonesResponse struct.
type FriendsGetByPhonesResponse []object.FriendsUserXtrPhone
// FriendsGetByPhones returns a list of the current user's friends
// whose phone numbers, validated or specified in a profile, are in a given list.
//
// https://vk.com/dev/friends.getByPhones
func (vk *VK) FriendsGetByPhones(params Params) (response FriendsGetByPhonesResponse, err error) {
err = vk.RequestUnmarshal("friends.getByPhones", &response, params)
return
}
// FriendsGetListsResponse struct.
type FriendsGetListsResponse struct {
Count int `json:"count"`
Items []object.FriendsFriendsList `json:"items"`
}
// FriendsGetLists returns a list of the user's friend lists.
//
// https://vk.com/dev/friends.getLists
func (vk *VK) FriendsGetLists(params Params) (response FriendsGetListsResponse, err error) {
err = vk.RequestUnmarshal("friends.getLists", &response, params)
return
}
// FriendsGetMutualResponse struct.
type FriendsGetMutualResponse []int
// FriendsGetMutual returns a list of user IDs of the mutual friends of two users.
//
// https://vk.com/dev/friends.getMutual
func (vk *VK) FriendsGetMutual(params Params) (response FriendsGetMutualResponse, err error) {
err = vk.RequestUnmarshal("friends.getMutual", &response, params)
return
}
// FriendsGetOnline returns a list of user IDs of a user's friends who are online.
//
// online_mobile=0
//
// https://vk.com/dev/friends.getOnline
func (vk *VK) FriendsGetOnline(params Params) (response []int, err error) {
err = vk.RequestUnmarshal("friends.getOnline", &response, params, Params{"online_mobile": false})
return
}
// FriendsGetOnlineOnlineMobileResponse struct.
type FriendsGetOnlineOnlineMobileResponse struct {
Online []int `json:"online"`
OnlineMobile []int `json:"online_mobile"`
}
// FriendsGetOnlineOnlineMobile returns a list of user IDs of a user's friends who are online.
//
// online_mobile=1
//
// https://vk.com/dev/friends.getOnline
func (vk *VK) FriendsGetOnlineOnlineMobile(params Params) (response FriendsGetOnlineOnlineMobileResponse, err error) {
err = vk.RequestUnmarshal("friends.getOnline", &response, params, Params{"online_mobile": true})
return
}
// FriendsGetRecentResponse struct.
type FriendsGetRecentResponse []int
// FriendsGetRecent returns a list of user IDs of the current user's recently added friends.
//
// https://vk.com/dev/friends.getRecent
func (vk *VK) FriendsGetRecent(params Params) (response FriendsGetRecentResponse, err error) {
err = vk.RequestUnmarshal("friends.getRecent", &response, params)
return
}
// FriendsGetRequestsResponse struct.
type FriendsGetRequestsResponse struct {
Count int `json:"count"` // Total requests number
Items []int `json:"items"`
}
// FriendsGetRequests returns information about the current user's incoming and outgoing friend requests.
//
// https://vk.com/dev/friends.getRequests
func (vk *VK) FriendsGetRequests(params Params) (response FriendsGetRequestsResponse, err error) {
reqParams := Params{
"need_mutual": false,
"extended": false,
}
err = vk.RequestUnmarshal("friends.getRequests", &response, params, reqParams)
return
}
// FriendsGetRequestsNeedMutualResponse struct.
type FriendsGetRequestsNeedMutualResponse struct {
Count int `json:"count"` // Total requests number
Items []object.FriendsRequests `json:"items"`
}
// FriendsGetRequestsNeedMutual returns information about the current user's incoming and outgoing friend requests.
//
// https://vk.com/dev/friends.getRequests
func (vk *VK) FriendsGetRequestsNeedMutual(params Params) (response FriendsGetRequestsNeedMutualResponse, err error) {
reqParams := Params{
"extended": false,
"need_mutual": true,
}
err = vk.RequestUnmarshal("friends.getRequests", &response, params, reqParams)
return
}
// FriendsGetRequestsExtendedResponse struct.
type FriendsGetRequestsExtendedResponse struct {
Count int `json:"count"`
Items []object.FriendsRequestsXtrMessage `json:"items"`
}
// FriendsGetRequestsExtended returns information about the current user's incoming and outgoing friend requests.
//
// https://vk.com/dev/friends.getRequests
func (vk *VK) FriendsGetRequestsExtended(params Params) (response FriendsGetRequestsExtendedResponse, err error) {
reqParams := Params{
"need_mutual": false,
"extended": true,
}
err = vk.RequestUnmarshal("friends.getRequests", &response, params, reqParams)
return
}
// FriendsGetSuggestionsResponse struct.
type FriendsGetSuggestionsResponse struct {
Count int `json:"count"`
Items []object.UsersUser `json:"items"`
}
// FriendsGetSuggestions returns a list of profiles of users whom the current user may know.
//
// https://vk.com/dev/friends.getSuggestions
func (vk *VK) FriendsGetSuggestions(params Params) (response FriendsGetSuggestionsResponse, err error) {
err = vk.RequestUnmarshal("friends.getSuggestions", &response, params)
return
}
// FriendsSearchResponse struct.
type FriendsSearchResponse struct {
Count int `json:"count"`
Items []object.UsersUser `json:"items"`
}
// FriendsSearch returns a list of friends matching the search criteria.
//
// https://vk.com/dev/friends.search
func (vk *VK) FriendsSearch(params Params) (response FriendsSearchResponse, err error) {
err = vk.RequestUnmarshal("friends.search", &response, params)
return
}

32
vendor/github.com/SevereCloud/vksdk/v2/api/gifts.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"
import "github.com/SevereCloud/vksdk/v2/object"
// GiftsGetResponse struct.
type GiftsGetResponse struct {
Count int `json:"count"`
Items []object.GiftsGift `json:"items"`
}
// GiftsGet returns a list of user gifts.
//
// https://vk.com/dev/gifts.get
func (vk *VK) GiftsGet(params Params) (response GiftsGetResponse, err error) {
err = vk.RequestUnmarshal("gifts.get", &response, params)
return
}
// GiftsGetCatalogResponse struct.
type GiftsGetCatalogResponse []struct {
Name string `json:"name"`
Title string `json:"title"`
Items []object.GiftsGift `json:"items"`
}
// GiftsGetCatalog returns catalog.
//
// https://vk.com/dev/gifts.get
func (vk *VK) GiftsGetCatalog(params Params) (response GiftsGetCatalogResponse, err error) {
err = vk.RequestUnmarshal("gifts.getCatalog", &response, params)
return
}

Some files were not shown because too many files have changed in this diff Show More