Compare commits

..

180 Commits

Author SHA1 Message Date
Wim
cca1ea2404 Release v1.17.0 (#1050) 2020-03-21 21:33:16 +01:00
Wim
281016a501 Fix duplicate separator on empty description/url (discord). Fixes #1008 (#1035)
Make this work for all possible cases.
Add tests
2020-03-21 21:27:17 +01:00
Qais Patankar
d4acdf2f89 Use blocks not attachments (slack) (#1048)
This removes the extra space below messages, as shown in
https://user-images.githubusercontent.com/923242/77235190-a3359980-6bab-11ea-8b7b-697d730ae5c1.png
2020-03-21 21:03:12 +01:00
Qais Patankar
0951e75c85 Fix #1039: messages sent to Slack being synced back (#1046)
This is a regression from https://github.com/42wim/matterbridge/pull/581#issuecomment-562937576

Behaves the same as 95190f11bf
2020-03-21 20:12:30 +01:00
Jakub
6b017b226a Support JSON and YAML config formats (#1045)
Signed-off-by: Jakub Sokołowski <jakub@status.im>
2020-03-18 23:20:29 +01:00
Qais Patankar
9e3bd7398c Fix #1027: warning when handling inbound webhooks (discord) (#1044) 2020-03-18 23:12:48 +01:00
Qais Patankar
79f764c7a8 Refactor webhook permission checks 2020-03-18 23:10:47 +01:00
Qais Patankar
b5dc4353fb Fix #1040: spotty webhook permission verification 2020-03-18 23:10:47 +01:00
Qais Patankar
2fbac73c29 Ignore ConnectingEvent (slack) (#1041) 2020-03-18 23:03:20 +01:00
burner1024
6616d105d1 Add markdownv2 mode for telegram documentation, see #1032 (#1037) 2020-03-18 22:43:11 +01:00
Wim
6b4b19194e Update vendor shazow/ssh-chat (#1029) 2020-03-08 23:55:09 +01:00
Wim
9785edd263 Remove replace directives and use own fork to make go get work again (#1028)
See https://github.com/golang/go/issues/30354
go get doesn't honor the go.mod replace options.
2020-03-08 17:08:18 +01:00
Qais Patankar
2a0bc11b68 Make some discord matterbridge.toml.sample lines less verbose 2020-03-08 14:30:54 +01:00
Qais Patankar
dd0325a88d Remove trailing newlines from matterbridge.toml.sample 2020-03-08 14:30:54 +01:00
Qais Patankar
20783c0978 Refactor matterbridge.toml.sample discord section 2020-03-08 14:30:54 +01:00
Wim
3f06a40bd5 Support code snippets from msteams 2020-03-01 22:19:33 +01:00
Wim
68f43985ad Add scopes again 2020-03-01 22:19:33 +01:00
Wim
915ca8f817 Make linter happy and cleanup (msteams) 2020-03-01 22:19:33 +01:00
Wim
a65a81610b Support threading from other bridges to msteams 2020-03-01 22:19:33 +01:00
Wim
8eb6ed5639 Support receiving attachments from msteams 2020-03-01 22:19:33 +01:00
Wim
795a8705c3 Add initial Microsoft Teams support
Documentation on https://github.com/42wim/matterbridge/wiki/MS-Teams-setup
2020-03-01 22:19:33 +01:00
Wim
3af0dc3b3a Vendor libraries needed for msteams support 2020-03-01 22:19:33 +01:00
Wim
9cf9b958a3 Do not lint gomnd (#1021) 2020-03-01 22:05:50 +01:00
Wim
3ac2ba8d5a Update to go1.14 and golangci-lint 1.23.7 (#1020) 2020-03-01 21:50:21 +01:00
Wim
d893421c7b Update vendor keybase/go-keybase-chat-bot (#1019) 2020-03-01 21:09:23 +01:00
Wim
250b3bb579 Use upstream slack-go/slack again (#1018) 2020-03-01 20:59:19 +01:00
Alexander Pushkov
e9edbfc051 Make Keybase link point to the team directly (#1013) 2020-02-19 21:02:44 +01:00
Wim
e343db6f72 Make avatars download work with mediaserverdownload (telegram). Fixes #920 (#1012) 2020-02-15 18:31:40 +01:00
Qais Patankar
4d57d66f85 Fix typo in feature_request.md (#1009) 2020-02-10 22:35:11 +01:00
Wim
54ed6320c2 Add support for avatars from matrix. #984 (#1007) 2020-02-10 00:06:54 +01:00
Wim
23083f3ae0 Rebase gomatrix vendor with upstream (#1006) 2020-02-09 23:49:17 +01:00
Wim
1985873494 Implement basic reconnect (whatsapp). Fixes #987 (#1003) 2020-02-09 22:11:46 +01:00
Qais Patankar
8ae5917659 Be less lossy when throttling IRC messages (#1004)
Note that msg.Text and chucking it through a chan is OK: https://play.golang.org/p/MTfT3YSsgPX
2020-02-09 22:10:18 +01:00
Qais Patankar
c91bfd08d8 Add ability to procure avatars from the destination bridge (#1000)
* remote_avatar: add UseLocalAvatar

* remote_avatar: make sure msg.Protocol is always set correctly

* remote_avatars: support msg.Account

* remote_avatar: add to matterbridge.toml.sample

* remote_avatar: clarify something
2020-02-09 22:07:26 +01:00
Qais Patankar
49110a5872 Assign automatically labels when creating issues (#1005)
* Update Bug_report.md

* Add 'label: enhancement' to feature_request.md
2020-02-09 22:03:53 +01:00
Wim
c01c8edeb8 Fix go-keybase-chat-bot api changes 2020-02-08 18:33:05 +01:00
Wim
ff8cf067b8 Update kekeybase/go-keybase-chat-bot vendor 2020-02-08 18:33:05 +01:00
Qais Patankar
1420f68050 Check only bridged channels for PermManageWebhooks (discord) (#1001)
* Check only bridged channels for PermManageWebhooks

* add note
2020-02-08 15:13:23 +01:00
Martijn Braam
c0be3e585a Enable intra-word emphasis supression in markdown (#999)
This fixes plain links sent to Matrix being broken if they contain
underscores. Fixes issue #997
2020-02-04 13:22:05 +01:00
Wim
3049ef9151 Bump version 2020-02-02 22:40:44 +01:00
Wim
4be00bbe6b Release v1.16.5 2020-02-02 22:36:07 +01:00
Wim
9382dde098 Release v1.16.4 2020-02-02 22:22:39 +01:00
Wim
1bf46b7711 Fix duplicated messages (sshchat). Fixes #950 (#996) 2020-02-02 22:08:37 +01:00
Wim
b85bae31d9 Show file comment in webhook if normal message is empty (discord). Fixes #962 (#995) 2020-02-02 21:14:54 +01:00
Patrizio Bekerle
0898829313 Add Docker Compose configuration (#990)
* Add Docker Compose configuration

* Add docker wiki link
2020-02-02 21:14:19 +01:00
Wim
f8ad877601 Add DisableWebPagePreview option (telegram). Closes #980 (#994) 2020-02-02 18:53:04 +01:00
Wim
585d1556c1 Disable smartypants in markdown parser. Fixes #989, #983 (#993) 2020-02-02 18:35:43 +01:00
Wim
7486555875 Fail with message instead of panic. #988 (#991) 2020-02-01 15:23:50 +01:00
Humorhenker
fc30b1bacc Add QuoteLengthLimit option (telegram) fixes #963 (#985)
* QuoteLengthLimit option added to limit max. quoted message length if QuoteLengthLimit = 0 the whole message will be quoted
2020-01-30 00:02:33 +01:00
c0ncord2
0dd19af6e8 Create outmessage-discordemoji.tengo (#979) 2020-01-30 00:00:57 +01:00
Wim
4c44515f9d Fix channel ID problem with multiple gateways (discord). Fixes #953 (#977) 2020-01-09 23:54:04 +01:00
Wim
9d84d6dd64 Update to tengo v2 (#976) 2020-01-09 21:52:19 +01:00
Wim
0f708daf2d Update dependencies (#975) 2020-01-09 21:02:56 +01:00
Wim
b9354de8fd Clean up go.mod and vendor 2020-01-09 18:21:10 +01:00
Guillaume Lazzara
c9d5f4c898 Add support for WhatsApp media (jpeg/png/gif) bridging (#974)
* Whatsapp image bridging

* Prevent double message in telegram when media with caption received

Co-authored-by: imShara <shara@protonmail.com>
2020-01-09 18:14:01 +01:00
c0ncord2
810c150781 move stripCustomoji logic to default Tengo script (#973)
*  move stripCustomoji logic to default Tengo script 

Removing the image ID from the message (without any possibility of recovering it later) is a loss of valuable data that prevents users from giving support to custom emoji via Tengo scripts.

* bugfix - do send colors to other irc bridges

"if we're not sending to an irc bridge we strip the IRC colors"

Co-authored-by: c0ncord <59654954+c0ncord@users.noreply.github.com>
2020-01-09 18:02:53 +01:00
Wim
31dd538c0b Add extra mimetypes to docker image. Fixes #969 2020-01-07 23:34:11 +01:00
Justin W. Flory
62e38e7c45 Add link to Ansible role for Matterbridge (#968)
This commit replaces the FOSSRIT/infrastructure link to the Matterbridge
role to a properly-defined Ansible role published in Ansible Galaxy. I
am the maintainer of the FOSSRIT/infrastructure repo and I decided to
split the Ansible role there into its own dedicated role. I figure this
might make it more accessible to others and also gives other folks a
chance to contribute. 😄

Signed-off-by: Justin W. Flory <git@jwf.io>
2020-01-01 21:38:26 +01:00
Wim
b9da28a29b Bump version 2019-12-16 00:01:00 +01:00
Wim
84bfa8a6b1 Release v1.16.3 2019-12-15 23:54:21 +01:00
Wim
1f830963f6 Return when we have only WebhookURL (mattermost). Fixes #954 (#960) 2019-12-15 23:49:17 +01:00
Wim
12d2c6fe89 Update slack vendor to fix regression (#959) 2019-12-08 21:05:02 +01:00
Wim
f43faf15f8 Update slack vendor to master (#958) 2019-12-07 22:54:36 +01:00
Wim
173a38a374 Bump version 2019-11-26 00:18:00 +01:00
Qais Patankar
1604ff15b5 Re-add binary to .gitignore (#951)
* Fix binary path and include windows
2019-11-26 00:16:40 +01:00
Wim
214fe502cd Release v1.16.2 2019-11-17 23:25:08 +01:00
Wim
aae45a8179 Upgrade linter and travis to go1.13 (#949) 2019-11-17 23:16:06 +01:00
Wim
075ca9ca47 Switch to new emoji library kyokomi/emoji (#948) 2019-11-17 23:01:03 +01:00
Wim
d4253d7a55 Update shazow/ssh-chat dependency (#947) 2019-11-17 21:42:41 +01:00
Benjamin
0917dc8766 Update markdown parsing library to github.com/gomarkdown/markdown (#944) 2019-11-17 21:18:01 +01:00
Wim
aba86855b5 Use own slack fork to fix #937 (#943) 2019-11-14 00:04:39 +01:00
Wim
ed5386c213 Add MatterAMXX link 2019-11-04 23:20:44 +01:00
Wim
455e75e92f Bump version 2019-11-01 22:32:39 +01:00
Gonçalo Ribeiro
c394de0c88 Add support for receiving attachments (keybase) (#923) 2019-11-01 22:29:52 +01:00
Wim
bad1990173 Release v1.16.1 2019-10-27 01:49:41 +02:00
Wim
0bc159341d Update vendor (#932)
* Update vendor

* Fix godiscord api change
2019-10-27 01:45:57 +02:00
Wim
45bf1fd63a Convert slack bold/strike to correct markdown (slack). Fixes #918 (#930) 2019-10-27 01:10:59 +02:00
Wim
ff0de85817 Remove obsolete file upload links (discord). Fixes #908 (#931)
Since v1.16.0 we now can upload files via webhook.
Old way of showing files with webhook only setup can be removed.
2019-10-27 01:10:43 +02:00
Wim
727fa9f929 Add support for uploading application/x and audio/x (matrix). Fixes #925 (#929) 2019-10-27 00:06:44 +02:00
Wim
0b9bc18236 Update vendor matterbridge/gomatrix fork (#928) 2019-10-26 23:31:44 +02:00
Wim
bad3b83d33 Update golang-commonmark/linkify vendor and use upstream again. Fixes #924 (#926) 2019-10-26 22:08:02 +02:00
Wim
00967a98ac Fix panic on WebhookURL only setting (mattermost). Closes #916 (#917) 2019-10-04 01:01:24 +02:00
Qais Patankar
1d708ab351 Suppress unhandled HelloEvent message (slack) (#913) 2019-10-04 00:19:50 +02:00
Qais Patankar
ba6759010b Add UserTypingSupport (discord) (#914)
* Add Discord to UserTypingSupport

* discord: start typing in a channel on EventUserTyping receive

* discord: emit EventUserTyping to gateway
2019-10-04 00:18:56 +02:00
Wim
da3868c104 Try to fix blackfriday go modules mess 2019-09-22 00:34:37 +02:00
Wim
0abf4d5d5d Specify correct GuildID on unknown user query (discord). Fixes #879 (#894) 2019-09-15 20:25:42 +02:00
Michal Suchánek
9b320cd43f Add token support (RocketChat) (#892)
Signed-off-by: Michal Suchanek <msuchanek@suse.de>
2019-09-13 23:41:02 +02:00
Wim
28783a4146 Do configuration validation on start-up. Fixes #888 (#889)
Fail if:
* we don't have any gateways configured
* we have gateways configured but with non-existing bridge configuration
* we have gateways configured without any configuration
2019-09-09 23:48:00 +02:00
Wim
f92927eae5 Fix deprecation in goreleaser 2019-09-07 23:37:49 +02:00
Wim
294139ce7a Bump version and fix changelog 2019-09-07 23:30:17 +02:00
Wim
45becd2573 Release v1.16.0 2019-09-07 23:17:55 +02:00
Wim
a3bee01e0a Update dependencies (#886) 2019-09-07 22:46:58 +02:00
David Buckley
1dc93ec4f0 Make getChannelIdTeam behave like GetChannelId for groups (mattermost) (#873)
GetChannelId will support names generated from query groups when a team is not set,
but not when a team is set since it falls through to getChannelIdTeam which has a different inner loop. i
This pull makes the two implementations do the same thing.
2019-09-07 21:39:44 +02:00
Wim
3562d4220c Bail if incorrect Jid (xmpp). Fixes #869 (#883) 2019-09-07 21:36:25 +02:00
Wim
1532f6e427 Update lrstanley/girc vendor (#884) 2019-09-07 21:35:45 +02:00
Wim
9327810bbf Add tengo example for nick color filter. See #881 2019-09-07 20:01:54 +02:00
Wim
f66d5f1e58 Add extra debug info (discord) 2019-09-05 22:39:43 +02:00
MOZGIII
cec086994e Add support for sending files via webhook (discord) (#872) 2019-08-29 00:13:10 +02:00
Wim
942d8f1ced Create .fixmie.yml 2019-08-26 23:49:06 +02:00
Wim
1552dcb143 Replace bwmarrin/discordgo with matterbridge/discordgo (#878)
Needed for #872
2019-08-26 23:47:50 +02:00
Wim
d525f1c9e4 Update Rhymen/go-whatsapp vendor (#876) 2019-08-26 23:22:34 +02:00
cori hudson
921f2dfcdf Add initial Keybase Chat support (#877)
* initial work on native keybase bridging

* Hopefully make a functional keybase bridge

* add keybase to bridgemap

* send to right channel, try to figure out received msgs

* add account and userid

* i am a Dam Fool

* Fix formatting for messages, handle /me

* update vendors, ran golint and goimports

* move handlers to handlers.go, clean up unused config options

* add sample config, fix inconsistent remote nick handling

* Update readme with keybase links

* Resolve fixmie errors

* Error -> Errorf

* fix linting errors in go.mod and go.sum

* explicitly join channels, ignore messages from non-specified channels

* check that team names match before bridging message
2019-08-26 21:00:31 +02:00
Wim
79a006c8de Fix regression (discord). Closes #864 (#866) 2019-07-29 23:37:38 +02:00
Wim
ff27746c0c Bump version 2019-07-15 23:23:32 +02:00
Wim
87788f354f Release v1.15.1 2019-07-15 23:09:46 +02:00
Wim
7d2e440c83 Add support for discord category channels (discord) (#863)
This adds support for the discord category option that can be used
to group channels in. This means we can have multiple channels with
the same name.

We add the option to specify a category in the channel option of a
discord account under [[gateway]]

Besides channel="channel" or channel="ID:channelID", now also
channel="category/channel" can be specified.

This change remains backwards compatible with people that haven't
specified the category and incorporates the fix in #861
2019-07-15 21:56:35 +02:00
Qais Patankar
5551f9d56f Fix discord channel & category name clash. #860 (#861) 2019-07-14 19:53:09 +02:00
Wim
1fb91c6316 Fix panic by checking slice bounds in handleEntities (telegram). Fixes #857 (#858)
Besides the bound checking, this now also use utf16 as suggested by
https://github.com/go-telegram-bot-api/telegram-bot-api/issues/231
2019-07-08 22:19:45 +02:00
Qais Patankar
e60949ff3f Support webhook message deletions (discord) (#853)
* Support webhook message deletions (discord)

Messages sent via webhook can now be deleted. It seems it can do this
without any special permissions.

This copies discordgo.WebhookExecute and makes it support the returning
of discordgo.Message.

A pull request has been sent upstream, so we should use that if
@bwmariin accepts the pull request:

https://github.com/bwmarrin/discordgo/pull/663

Changes in behaviour (webhook mode only):
- Previously messages *edited* on other platforms would just be
retransmitted as a brand new message to Discord.
- Message *edits* will now be ignored.
- Debug: message edits will now print out a "permission error".

In the future it may be good to send an "message edited" react to those
webhook messages, so at least people know that the message was edited on
other platforms. (Even though it can't actually show the new message.)

Alternatively, message edits could just send a brand new message with a
link back to the old one. This is a little ugly but it would ensure that
Discord users are able to see the edited message. These "message edit
notifications" would be sent from the bot user (not from a webhook), so
we could edit the "edit notification" if subsequent edits to the
original message are made.
2019-07-08 22:18:37 +02:00
Wim
278a3c6890 Update changelog 2019-06-30 18:50:45 +02:00
Wim
fcf734eb36 Update to golanci-lint v1.17.1 2019-06-30 18:43:54 +02:00
Wim
cf3cddafab Keep connection state. Fixes #856
Actually check if we're connected when trying to Send() a message.
Messages now will get dropped when not connected.

TODO: Ideally this should be in a ring buffer to retransmit when the
connection comes back up.
2019-06-30 18:34:41 +02:00
Wim
c52664f22e Update readme 2019-06-16 23:46:24 +02:00
Wim
cb712ff37d Update vendor (#852) 2019-06-16 23:33:25 +02:00
Qais Patankar
f4ae610448 Add .gitignore (#850) 2019-06-16 16:37:38 +02:00
Wim
601b8bc98d Update documentation and changelog 2019-06-16 16:32:12 +02:00
Joona Hoikkala
80b4cec87a Add an option to skip the Mattermost server version check (#849)
Adds SkipVersionCheck bool option for mattermost
2019-06-16 16:23:50 +02:00
Qais Patankar
76c7b69e4e Support bulk deletions (discord) 2019-06-16 16:07:48 +02:00
Wim
a5bd3c4dda Bump version 2019-06-16 16:02:41 +02:00
Wim
f06e9b5605 Release v1.15.0 2019-06-14 01:36:55 +02:00
Nick
7a3bb0e55c Verify TLS against JID domain, not the host. (xmpp) (#834)
Partially fixes #820.

A full fix requires patching https://github.com/matterbridge/go-xmpp to use DNS SRV records.
2019-06-14 01:10:43 +02:00
Wim
6e8f535e8b Fix logic (xmpp) 2019-06-14 00:44:31 +02:00
Wim
5619a75b05 Fix regression in autojoining with legacy tokens (slack). Fixes #651 (#848) 2019-06-14 00:42:55 +02:00
Wim
53dfb78215 Allow messages with timestamp (xmpp). Fixes #835 (#847) 2019-06-14 00:24:42 +02:00
Wim
8e97cbab1e Fix noisy whatsapp error logging 2019-06-14 00:02:32 +02:00
Wim
ce7b749fd5 Update github.com/Rhymen/go-whatsapp vendor. Fixes #843 2019-06-14 00:02:32 +02:00
Wim
6617bd6609 Revert xmpp to orig behaviour. Closes #844 2019-06-13 23:35:04 +02:00
Wim
e610fb3201 Make config parse errors readable 2019-06-02 09:35:20 +01:00
Wim
40f1d35415 Fix go mod issue by removing whatsapp-ext 2019-06-02 09:35:20 +01:00
Duco van Amstel
b79bf7d414 Forward only user-typing messages if supported by protocol (#832)
Fixes issue #814.

This is a somewhat hacky way of achieving the required goal but it seems
like this is the least problematic way of getting there.

We might want to redesign some bridge information later such that we
have a standardised way of specifying what is and what isn't supported
by each chat protocol / bridge.
2019-05-30 15:00:40 +02:00
Duco van Amstel
3724cc3a15 Clean-up XMPP handling code (#831) 2019-05-30 12:31:54 +02:00
Wim
3418e8c9af Use upstream whatsapp again (#809) 2019-05-30 12:20:56 +02:00
Duco van Amstel
9619dff334 Linter fixes 2019-05-27 17:38:31 +01:00
Wim
1b2feb19e5 Update channels of all teams (mattermost) 2019-05-02 00:46:49 +02:00
Wim
1829dc3d9f Allow messages from other bots (discord). Fixes #816 2019-05-01 18:10:31 +02:00
Wim
bd0e81f5a0 Add msg event to tengo 2019-04-24 22:47:37 +02:00
Wim
f04d360ee2 Update README with v1.14.4 2019-04-23 23:36:30 +02:00
Wim
92f27281fa Update changelog 2019-04-23 23:35:48 +02:00
Wim
65781b9316 Disable user lookups on delete messages (slack) (#812) 2019-04-23 23:29:15 +02:00
Duco van Amstel
9be0be0316 Add lacking clean-up in Slack synchronisation (#811) 2019-04-23 23:08:34 +02:00
Wim
9f5f004725 Use paging in initUser and UpdateUsers (mattermost) 2019-04-20 23:06:06 +02:00
Wim
fed77cccf3 Handle unthreaded messages (mattermost). Fixes #803 2019-04-19 23:31:45 +02:00
Wim
9b520dfb78 Fix panic on nil message.Post (mattermost). Fixes #804 2019-04-19 23:08:41 +02:00
Wim
8ad2be10b2 Add Id to EditMessage (mattermost). Fixes #802 2019-04-19 22:59:04 +02:00
Wim
2d277a15f5 Add scripting (tengo) support for every outgoing message (#806)
Adds a new key OutMessage under [tengo] table, which specifies the location of the script that
will be invoked on each message being sent to a bridge and can be used to modify the Username
and the Text of that message.

The script will have the following global variables:
read-only:
inAccount, inProtocol, inChannel, inGateway
outAccount, outProtocol, outChannel, outGateway

read-write:
msgText, msgUsername

The script is reloaded on every message, so you can modify the script on the fly.

The default script in https://github.com/42wim/matterbridge/tree/master/internal/tengo/outmessage.tengo
is compiled in and will be executed if no script is specified.
2019-04-19 18:27:31 +02:00
Wim
d60468bb05 Bump version 2019-04-19 18:24:13 +02:00
Wim
82d6210464 Update changelog 2019-04-19 18:23:50 +02:00
Wim
ff198042d2 Remove deprecated TengoModifyMessage
This has become InMessage under [tengo]
2019-04-19 00:13:28 +02:00
chotaire
6b47e29583 Add verbose IRC joins/parts (ident@host) (#805)
New configuration setting: VerboseJoinPart (default is false)
2019-04-18 23:56:05 +02:00
Wim
380c38674c Fix deadlock on reconnect (irc). Closes #757 2019-04-15 23:28:47 +02:00
Wim
3c14a0891e Remove hipchat 2019-04-14 23:54:05 +02:00
Wim
8513a07416 Update README 2019-04-14 23:48:54 +02:00
Qais Patankar
220485a849 Add remotenickformat-zerowidth.tengo to contrib (#799) 2019-04-14 23:42:16 +02:00
Wim
4db34b0506 Send channel_created and deleted event through message channel (mattermost) 2019-04-13 21:52:39 +02:00
Wim
5677c912a8 Add useraction support (rocketchat). Closes #772 (#794) 2019-04-08 23:30:22 +02:00
Wim
7a24de15e4 Add tengo support to RemoteNickFormat (#793)
This commit add support for using the result of a tengo script in RemoteNickFormat using {TENGO}
Also adds a new toml table [tengo] with key RemoteNickFormat and value location of the script.
This also moves the TengoModifyMessage from [general] to Message in [tengo]

Documentation:

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

The result will be set in {TENGO} in the RemoteNickFormat key of every bridge where {TENGO} is specified
The script is reloaded on every message, so you can modify the script on the fly.
Example script can be found in https://github.com/42wim/matterbridge/tree/master/contrib/remotenickformat.tengo

[tengo]
RemoteNickFormat="remotenickformat.tengo"
2019-04-08 20:58:21 +02:00
Wim
99d9ea283a Build on every branch (travis) 2019-04-07 23:57:47 +02:00
Wim
dac92a0e0a Add xmpp room to README. Closes #758 2019-04-07 15:48:19 +02:00
Wim
a25efb16f3 Bump version 2019-04-07 15:41:14 +02:00
Wim
e4d73b29a1 Release v1.14.2 2019-04-06 23:29:49 +02:00
Wim
8a875f292e Revert fix for #722. Closes #781
Revert "Fix typo"

This reverts commit dffd67eb31.

Revert "Handle quit message relay better on gateways with one channel on the irc bridge #722"

This reverts commit 240559581a.

Revert "Support quits from irc correctly. Fixes #722 (#724)"

This reverts commit d76a04bd0a.
2019-04-06 23:12:48 +02:00
Wim
60a85621ea Return when not connected and drop a message (irc). Fixes #786 2019-04-06 22:34:41 +02:00
Wim
115d20373c Update tengo vendor and load the stdlib. Fixes #789 (#792) 2019-04-06 22:18:25 +02:00
Wim
cdf33e5748 Use default nick if none specified (irc). Fixes #785 2019-04-05 00:17:46 +02:00
Wim
01d0a9f412 Handle nil message (telegram). Fixes #777 2019-04-05 00:04:08 +02:00
Wim
8cc2d3b4fe Revert "Bail if any vars are nil, not if all (telegram) (#778)"
This reverts commit efd2c99862.
2019-04-05 00:02:26 +02:00
Wim
aba9e4f3be Fix travis before_deploy 2019-04-04 23:22:56 +02:00
Wim
4d575ba13a Fix travis deploy condition and update to golangci-lint v1.16 2019-04-04 23:05:58 +02:00
Duco van Amstel
7f0e4ad448 Add CI fixes and improvements (#780)
* Update GolangCI-lint and lint config

The `algo` parameter for the `unparam` linter has been removed and we
should thus no longer specify it. Also, bumping the GolangCI-lint
version to the latest available minor release.

See: mvdan/unparam@e6a6d1c51b

* Fix and improve bintray CI script

* Further CI setup improvements

* Split-out CI steps into stand-alone scripts
2019-04-04 22:54:51 +02:00
Wim
17cc14a9d2 Send user_added and removed event through message channel (mattermost) 2019-04-02 00:15:58 +02:00
Wim
1f8016182c Return channelId for other channeltypes too (mattermost) 2019-04-01 22:50:19 +02:00
Wim
caf9ef2c4b Bump travis to go 1.12.x 2019-03-27 23:22:55 +01:00
Wim
64b57f2da3 Ignore message_replied and hidden messages (slack). Fixes #709 (#779) 2019-03-27 22:54:18 +01:00
David Hill
efd2c99862 Bail if any vars are nil, not if all (telegram) (#778) 2019-03-27 21:00:57 +01:00
Wim
cc05ba8907 Thank DigitalOcean (https://digitalocean.com) for another year of sponsorship 2019-03-25 21:13:11 +01:00
Wim
16763b715a Look up #channel too (rocketchat). Fix #773 (#775) 2019-03-24 20:15:15 +01:00
Wim
ffaa598796 Bump version 2019-03-24 19:52:31 +01:00
Wim
858e16d34f Release v1.14.1 2019-03-21 21:07:11 +01:00
Wim
a60e62efb1 Update doc wrt rocketchat api issue 2019-03-21 21:05:27 +01:00
David Hill
97f9d4be67 Fix double unlock (slack) (#771) 2019-03-21 17:30:28 +01:00
4669 changed files with 467851 additions and 145114 deletions

3
.fixmie.yml Normal file
View File

@@ -0,0 +1,3 @@
go:
comments:
disabled: true

View File

@@ -1,6 +1,7 @@
---
name: Bug report
about: Create a report to help us improve. (Check the FAQ on the wiki first)
labels: bug
---

View File

@@ -1,6 +1,7 @@
---
name: Feature request
about: Suggest an idea for this project
labels: enhancement
---

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# Exclude matterbridge binary
/matterbridge
/matterbridge.exe
# Exclude configuration file
matterbridge.toml

View File

@@ -7,7 +7,7 @@ run:
# concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 1m
deadline: 2m
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
@@ -105,10 +105,6 @@ linters-settings:
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unparam:
# call graph construction algorithm (cha, rta). In general, use cha for libraries,
# and rta for programs with main packages. Default is cha.
algo: rta
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
@@ -178,6 +174,8 @@ linters:
- lll
- maligned
- prealloc
- wsl
- gomnd
# rules to deal with reported isues

View File

@@ -21,14 +21,18 @@ builds:
ldflags:
- -s -w -X main.githash={{.ShortCommit}}
archive:
name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format: binary
files:
- none*
replacements:
386: 32bit
amd64: 64bit
archives:
-
id: matterbridge
builds:
- matterbridge
name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format: binary
files:
- none*
replacements:
386: 32bit
amd64: 64bit
checksum:
name_template: 'checksums.txt'

View File

@@ -1,75 +1,56 @@
language: go
go:
- 1.11.x
go_import_path: github.com/42wim/matterbridge
# we have everything vendored
# We have everything vendored so this helps TravisCI not run `go get ...`.
install: true
git:
depth: 200
env:
global:
- GOOS=linux GOARCH=amd64
- GO111MODULE=on
- GOLANGCI_VERSION="v1.14.0"
matrix:
# It's ok if our code fails on unstable development versions of Go.
allow_failures:
- go: tip
# Don't wait for tip tests to finish. Mark the test run green if the
# tests pass on the stable versions of Go.
fast_finish: true
notifications:
email: false
before_script:
# Get version info from tags.
- MY_VERSION="$(git describe --tags)"
# Retrieve the golangci-lint linter binary.
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION}
# Retrieve and prepare CodeClimate's test coverage reporter.
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script:
# Ensure that the module files are being kept correctly and that vendored dependencies are up-to-date.
- go mod tidy
- go mod vendor
- git diff --exit-code --quiet || (echo "Please run 'go mod tidy' to clean up the 'go.mod' and 'go.sum' files."; false)
# Run the linter.
- golangci-lint run
# Run all the tests with the race detector and generate coverage.
- go test -v -race -coverprofile c.out ./...
# Run the build script to generate the necessary binaries and images.
- /bin/bash ci/bintray.sh
after_script:
# Upload test coverage to CodeClimate.
- ./cc-test-reporter after-build --exit-code ${TRAVIS_TEST_RESULT}
email: false
branches:
only:
- master
- master
- /.*/
jobs:
include:
- stage: lint
# Run linting in one Go environment only.
script: ./ci/lint.sh
go: 1.14.x
env:
- GO111MODULE=on
- GOLANGCI_VERSION="v1.23.7"
- stage: test
# Run tests in a combination of Go environments.
script: ./ci/test.sh
go: 1.13.x
env:
- GOFLAGS=-mod=vendor
- script: ./ci/test.sh
go: 1.13.x
env:
- GO111MODULE=on
- script: ./ci/test.sh
go: 1.14.x
env:
- GO111MODULE=on
- REPORT_COVERAGE=1
- BINDEPLOY=1
before_deploy: /bin/bash ci/bintray.sh
deploy:
on:
all_branches: true
provider: bintray
on:
all_branches: true
condition: $BINDEPLOY = 1
provider: bintray
edge:
branch: v1.8.47
file: ci/deploy.json
user: 42wim
on:
all_branches: true
key:
secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI="
secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI="

View File

@@ -2,7 +2,7 @@ FROM alpine:edge
ENTRYPOINT ["/bin/matterbridge"]
COPY . /go/src/github.com/42wim/matterbridge
RUN apk update && apk add go git gcc musl-dev ca-certificates \
RUN apk update && apk add go git gcc musl-dev ca-certificates mailcap \
&& cd /go/src/github.com/42wim/matterbridge \
&& export GOPATH=/go \
&& go get \

349
README.md
View File

@@ -3,158 +3,198 @@
# matterbridge
![Matterbridge Logo](img/matterbridge-notext.gif)<br />
**A simple chat bridge**<br />
Letting people be where they want to be.<br />
<sub>Bridges between a growing number of protocols. Click below to demo or join the development chat.</sub>
**A simple chat bridge**<br />
Letting people be where they want to be.<br />
<sub>Bridges between a growing number of protocols. Click below to demo or join the development chat.</sub>
<sup>
[Gitter][mb-gitter] |
[IRC][mb-irc] |
[Discord][mb-discord] |
[Matrix][mb-matrix] |
[Slack][mb-slack] |
[Mattermost][mb-mattermost] |
[Rocket.Chat][mb-rocketchat] |
[XMPP][mb-xmpp] |
[Twitch][mb-twitch] |
[WhatsApp][mb-whatsapp] |
[Zulip][mb-zulip] |
[Telegram][mb-telegram] |
And more...
</sup>
[Gitter][mb-gitter] |
[IRC][mb-irc] |
[Discord][mb-discord] |
[Matrix][mb-matrix] |
[Slack][mb-slack] |
[Mattermost][mb-mattermost] |
[Rocket.Chat][mb-rocketchat] |
[XMPP][mb-xmpp] |
[Twitch][mb-twitch] |
[WhatsApp][mb-whatsapp] |
[Zulip][mb-zulip] |
[Telegram][mb-telegram] |
[Keybase][mb-keybase] |
And more...
</sup>
---
----
[![Download stable](https://img.shields.io/github/release/42wim/matterbridge.svg?label=download%20stable)](https://github.com/42wim/matterbridge/releases/latest)
[![Download dev](https://img.shields.io/bintray/v/42wim/nightly/Matterbridge.svg?label=download%20dev&colorB=007ec6)](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
[![Maintainability](https://api.codeclimate.com/v1/badges/82dff70ef2ba85a6173a/maintainability)](https://codeclimate.com/github/42wim/matterbridge/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/82dff70ef2ba85a6173a/test_coverage)](https://codeclimate.com/github/42wim/matterbridge/test_coverage)<br />
[![Download dev](https://img.shields.io/bintray/v/42wim/nightly/Matterbridge.svg?label=download%20dev&colorB=007ec6)](https://bintray.com/42wim/nightly/Matterbridge/_latestVersion)
[![Maintainability](https://api.codeclimate.com/v1/badges/82dff70ef2ba85a6173a/maintainability)](https://codeclimate.com/github/42wim/matterbridge/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/82dff70ef2ba85a6173a/test_coverage)](https://codeclimate.com/github/42wim/matterbridge/test_coverage)<br />
<hr />
</div>
<div align="right"><sup>
**Note:** Matter<em>most</em> isn't required to run matter<em>bridge</em>.</sup></div>
### Table of Contents
* [Features](https://github.com/42wim/matterbridge/wiki/Features)
* [Natively supported](#natively-supported)
* [3rd party via matterbridge api](#3rd-party-via-matterbridge-api)
* [API](#API)
* [Chat with us](#chat-with-us)
* [Screenshots](https://github.com/42wim/matterbridge/wiki/)
* [Installing](#installing)
* [Binaries](#binaries)
* [Building](#building)
* [Configuration](#configuration)
* [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config)
* [Examples](#examples)
* [Running](#running)
* [Docker](#docker)
* [Changelog](#changelog)
* [FAQ](#faq)
* [Related projects](#related-projects)
* [Articles](#articles)
* [Thanks](#thanks)
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_blue.svg" width="201px">
</a>
</p>
# Table of Contents
- [matterbridge](#matterbridge)
- [Table of Contents](#table-of-contents)
- [Features](#features)
- [Natively supported](#natively-supported)
- [3rd party via matterbridge api](#3rd-party-via-matterbridge-api)
- [API](#api)
- [Chat with us](#chat-with-us)
- [Screenshots](#screenshots)
- [Installing / upgrading](#installing--upgrading)
- [Binaries](#binaries)
- [Packages](#packages)
- [Building](#building)
- [Configuration](#configuration)
- [Basic configuration](#basic-configuration)
- [Settings](#settings)
- [Advanced configuration](#advanced-configuration)
- [Examples](#examples)
- [Bridge mattermost (off-topic) - irc (#testing)](#bridge-mattermost-off-topic---irc-testing)
- [Bridge slack (#general) - discord (general)](#bridge-slack-general---discord-general)
- [Running](#running)
- [Docker](#docker)
- [Changelog](#changelog)
- [FAQ](#faq)
- [Related projects](#related-projects)
- [Articles](#articles)
- [Thanks](#thanks)
## Features
* [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols)
* [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols)
* [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes)
* Preserves threading when possible
* [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling)
* [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing)
* [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups)
* [API](https://github.com/42wim/matterbridge/wiki/Features#api)
- [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols)
- [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols)
- [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes)
- Preserves threading when possible
- [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling)
- [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing)
- [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups)
- [API](https://github.com/42wim/matterbridge/wiki/Features#api)
### Natively supported
* [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
* [IRC](http://www.mirc.com/servers.html)
* [XMPP](https://xmpp.org)
* [Gitter](https://gitter.im)
* [Slack](https://slack.com)
* [Discord](https://discordapp.com)
* [Telegram](https://telegram.org)
* [Hipchat](https://www.hipchat.com)
* [Rocket.chat](https://rocket.chat)
* [Matrix](https://matrix.org)
* [Steam](https://store.steampowered.com/)
* [Twitch](https://twitch.tv)
* [Ssh-chat](https://github.com/shazow/ssh-chat)
* [WhatsApp](https://www.whatsapp.com/)
* [Zulip](https://zulipchat.com)
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
- [IRC](http://www.mirc.com/servers.html)
- [XMPP](https://xmpp.org)
- [Gitter](https://gitter.im)
- [Slack](https://slack.com)
- [Discord](https://discordapp.com)
- [Telegram](https://telegram.org)
- [Rocket.chat](https://rocket.chat)
- [Matrix](https://matrix.org)
- [Steam](https://store.steampowered.com/)
- [Twitch](https://twitch.tv)
- [Ssh-chat](https://github.com/shazow/ssh-chat)
- [WhatsApp](https://www.whatsapp.com/)
- [Zulip](https://zulipchat.com)
- [Keybase](https://keybase.io)
### 3rd party via matterbridge api
* [Minecraft](https://github.com/elytra/MatterLink)
* [Reddit](https://github.com/bonehurtingjuice/mattereddit)
* [Facebook messenger](https://github.com/VictorNine/fbridge)
* [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Minecraft](https://github.com/elytra/MatterLink)
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
### API
The API is very basic at the moment.
The API is basic at the moment.
More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/Api).
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)
* [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)
- [MatterLink](https://github.com/elytra/MatterLink) (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)
## Chat with us
Questions or want to test on your favorite platform? Join below:
* [Gitter][mb-gitter]
* [IRC][mb-irc]
* [Discord][mb-discord]
* [Matrix][mb-matrix]
* [Slack][mb-slack]
* [Mattermost][mb-mattermost]
* [Rocket.Chat][mb-rocketchat]
* [XMPP][mb-xmpp]
* [Twitch][mb-twitch]
* [Zulip][mb-zulip]
* [Telegram][mb-telegram]
- [Gitter][mb-gitter]
- [IRC][mb-irc]
- [Discord][mb-discord]
- [Matrix][mb-matrix]
- [Slack][mb-slack]
- [Mattermost][mb-mattermost]
- [Rocket.Chat][mb-rocketchat]
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
- [Twitch][mb-twitch]
- [Zulip][mb-zulip]
- [Telegram][mb-telegram]
- [Keybase][mb-keybase]
## Screenshots
See https://github.com/42wim/matterbridge/wiki
## Installing
## Installing / upgrading
### Binaries
* Latest stable release [v1.14.0](https://github.com/42wim/matterbridge/releases/latest)
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
- Latest stable release [v1.17.0](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
### Packages
* [Overview](https://repology.org/metapackage/matterbridge/versions)
### Building
Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
- [Overview](https://repology.org/metapackage/matterbridge/versions)
## Building
Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest)
If you really want to build from source, follow these instructions:
Go 1.12+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
After Go is setup, download matterbridge to your $GOPATH directory.
```
cd $GOPATH
go get github.com/42wim/matterbridge
```
You should now have matterbridge binary in the bin directory:
You should now have matterbridge binary in the ~/go/bin directory:
```
$ ls bin/
$ ls ~/go/bin/
matterbridge
```
## Configuration
### Basic configuration
See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
### Settings
All possible [settings](https://github.com/42wim/matterbridge/wiki/Settings) for each bridge.
### Advanced configuration
* [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
- [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
### Examples
#### Bridge mattermost (off-topic) - irc (#testing)
```toml
[irc]
[irc.freenode]
@@ -183,6 +223,7 @@ enable=true
```
#### Bridge slack (#general) - discord (general)
```toml
[slack]
[slack.test]
@@ -227,12 +268,11 @@ Usage of ./matterbridge:
```
### Docker
Create your matterbridge.toml file locally eg in `/tmp/matterbridge.toml`
```
docker run -ti -v /tmp/matterbridge.toml:/matterbridge.toml 42wim/matterbridge
```
Please take a look at the [Docker Wiki page](https://github.com/42wim/matterbridge/wiki/Deploy:-Docker) for more information.
## Changelog
See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.md)
## FAQ
@@ -240,61 +280,72 @@ See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.m
See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
## Related projects
* [FOSSRIT/infrastructure - roles/matterbridge](https://github.com/FOSSRIT/infrastructure/tree/master/roles/matterbridge) (Ansible role used to automate deployments of Matterbridge)
* [matterbridge autoconfig](https://github.com/patcon/matterbridge-autoconfig)
* [matterbridge config viewer](https://github.com/patcon/matterbridge-heroku-viewer)
* [matterbridge-heroku](https://github.com/cadecairos/matterbridge-heroku)
* [mattereddit](https://github.com/bonehurtingjuice/mattereddit)
* [matterlink](https://github.com/elytra/MatterLink)
* [mattermost-plugin](https://github.com/matterbridge/mattermost-plugin) - Run matterbridge as a plugin in mattermost
* [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot)
* [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)
- [jwflory/ansible-role-matterbridge](https://galaxy.ansible.com/jwflory/matterbridge) (Ansible role to simplify deploying Matterbridge)
- [matterbridge autoconfig](https://github.com/patcon/matterbridge-autoconfig)
- [matterbridge config viewer](https://github.com/patcon/matterbridge-heroku-viewer)
- [matterbridge-heroku](https://github.com/cadecairos/matterbridge-heroku)
- [mattereddit](https://github.com/bonehurtingjuice/mattereddit)
- [matterlink](https://github.com/elytra/MatterLink)
- [mattermost-plugin](https://github.com/matterbridge/mattermost-plugin) - Run matterbridge as a plugin in mattermost
- [pyCord](https://github.com/NikkyAI/pyCord) (crossplatform chatbot)
- [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)
## Articles
* [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/
- [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/
## Thanks
[![Digitalocean](https://snag.gy/3LVifX.jpg)](https://www.digitalocean.com/) for sponsoring demo/testing droplets.
<p>This project is supported by:</p>
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
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
* mattermost - https://github.com/mattermost/mattermost-server
* matrix - https://github.com/matrix-org/gomatrix
* sshchat - https://github.com/shazow/ssh-chat
* slack - https://github.com/nlopes/slack
* steam - https://github.com/Philipp15b/go-steam
* telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
* xmpp - https://github.com/mattn/go-xmpp
* whatsapp - https://github.com/Rhymen/go-whatsapp/
* zulip - https://github.com/ifo/gozulipbot
* tengo - https://github.com/d5/tengo
- 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
- mattermost - https://github.com/mattermost/mattermost-server
- matrix - https://github.com/matrix-org/gomatrix
- sshchat - https://github.com/shazow/ssh-chat
- slack - https://github.com/nlopes/slack
- steam - https://github.com/Philipp15b/go-steam
- telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
- xmpp - https://github.com/mattn/go-xmpp
- whatsapp - https://github.com/Rhymen/go-whatsapp/
- zulip - https://github.com/ifo/gozulipbot
- tengo - https://github.com/d5/tengo
- keybase - https://github.com/keybase/go-keybase-chat-bot
<!-- Links -->
[mb-gitter]: https://gitter.im/42wim/matterbridge
[mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat
[mb-discord]: https://discord.gg/AkKPtrQ
[mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
[mb-xmpp]: https://inverse.chat/
[mb-twitch]: https://www.twitch.tv/matterbridge
[mb-whatsapp]: https://www.whatsapp.com/
[mb-zulip]: https://matterbridge.zulipchat.com/register/
[mb-telegram]: https://t.me/Matterbridge
[mb-gitter]: https://gitter.im/42wim/matterbridge
[mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat
[mb-discord]: https://discord.gg/AkKPtrQ
[mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
[mb-xmpp]: https://inverse.chat/
[mb-twitch]: https://www.twitch.tv/matterbridge
[mb-whatsapp]: https://www.whatsapp.com/
[mb-keybase]: https://keybase.io/team/matterbridge
[mb-zulip]: https://matterbridge.zulipchat.com/register/
[mb-telegram]: https://t.me/Matterbridge

View File

@@ -1,6 +1,7 @@
package bridge
import (
"log"
"strings"
"sync"
@@ -41,6 +42,10 @@ type Factory func(*Config) Bridger
func New(bridge *config.Bridge) *Bridge {
accInfo := strings.Split(bridge.Account, ".")
if len(accInfo) != 2 {
log.Fatalf("config failure, account incorrect: %s", bridge.Account)
}
protocol := accInfo[0]
name := accInfo[1]

View File

@@ -3,6 +3,7 @@ package config
import (
"bytes"
"io/ioutil"
"path/filepath"
"strings"
"sync"
"time"
@@ -76,9 +77,11 @@ type Protocol struct {
BindAddress string // mattermost, slack // DEPRECATED
Buffer int // api
Charset string // irc
ClientID string // msteams
ColorNicks bool // only irc for now
Debug bool // general
DebugLevel int // only for irc now
DisableWebPagePreview bool // telegram
EditSuffix string // mattermost, slack, discord, telegram, gitter
EditDisable bool // mattermost, slack, discord, telegram, gitter
IconURL string // mattermost, slack
@@ -116,31 +119,38 @@ type Protocol struct {
Protocol string // all protocols
QuoteDisable bool // telegram
QuoteFormat string // telegram
QuoteLengthLimit int // telegram
RejoinDelay int // IRC
ReplaceMessages [][]string // all protocols
ReplaceNicks [][]string // all protocols
RemoteNickFormat string // all protocols
RunCommands []string // irc
RunCommands []string // IRC
Server string // IRC,mattermost,XMPP,discord
SessionFile string // msteams,whatsapp
ShowJoinPart bool // all protocols
ShowTopicChange bool // slack
ShowUserTyping bool // slack
ShowEmbeds bool // discord
SkipTLSVerify bool // IRC, mattermost
SkipVersionCheck bool // mattermost
StripNick bool // all protocols
SyncTopic bool // slack
TengoModifyMessage string // general
Team string // mattermost
Team string // mattermost, keybase
TeamID string // msteams
TenantID string // msteams
Token string // gitter, slack, discord, api
Topic string // zulip
URL string // mattermost, slack // DEPRECATED
UseAPI bool // mattermost, slack
UseLocalAvatar []string // discord
UseSASL bool // IRC
UseTLS bool // IRC
UseDiscriminator bool // discord
UseFirstName bool // telegram
UseUserName bool // discord
UseInsecureURL bool // telegram
VerboseJoinPart bool // IRC
WebhookBindAddress string // mattermost, slack
WebhookURL string // mattermost, slack
}
@@ -166,6 +176,13 @@ type Gateway struct {
InOut []Bridge
}
type Tengo struct {
InMessage string
Message string
RemoteNickFormat string
OutMessage string
}
type SameChannelGateway struct {
Name string
Enable bool
@@ -189,12 +206,15 @@ type BridgeValues struct {
SSHChat map[string]Protocol
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
General Protocol
Tengo Tengo
Gateway []Gateway
SameChannelGateway []SameChannelGateway
}
type Config interface {
Viper() *viper.Viper
BridgeValues() *BridgeValues
GetBool(key string) (bool, bool)
GetInt(key string) (int, bool)
@@ -221,7 +241,8 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
logger.Fatalf("Failed to read configuration file: %#v", err)
}
mycfg := newConfigFromString(logger, input)
cfgtype := detectConfigType(cfgfile)
mycfg := newConfigFromString(logger, input, cfgtype)
if mycfg.cv.General.MediaDownloadSize == 0 {
mycfg.cv.General.MediaDownloadSize = 1000000
}
@@ -232,25 +253,37 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
return mycfg
}
// detectConfigType detects JSON and YAML formats, defaults to TOML.
func detectConfigType(cfgfile string) string {
fileExt := filepath.Ext(cfgfile)
switch fileExt {
case ".json":
return "json"
case ".yaml", ".yml":
return "yaml"
}
return "toml"
}
// NewConfigFromString instantiates a new configuration based on the specified string.
func NewConfigFromString(rootLogger *logrus.Logger, input []byte) Config {
logger := rootLogger.WithFields(logrus.Fields{"prefix": "config"})
return newConfigFromString(logger, input)
return newConfigFromString(logger, input, "toml")
}
func newConfigFromString(logger *logrus.Entry, input []byte) *config {
viper.SetConfigType("toml")
func newConfigFromString(logger *logrus.Entry, input []byte, cfgtype string) *config {
viper.SetConfigType(cfgtype)
viper.SetEnvPrefix("matterbridge")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
viper.AutomaticEnv()
if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil {
logger.Fatalf("Failed to parse the configuration: %#v", err)
logger.Fatalf("Failed to parse the configuration: %s", err)
}
cfg := &BridgeValues{}
if err := viper.Unmarshal(cfg); err != nil {
logger.Fatalf("Failed to load the configuration: %#v", err)
logger.Fatalf("Failed to load the configuration: %s", err)
}
return &config{
logger: logger,
@@ -263,6 +296,10 @@ func (c *config) BridgeValues() *BridgeValues {
return c.cv
}
func (c *config) Viper() *viper.Viper {
return c.v
}
func (c *config) GetBool(key string) (bool, bool) {
c.RLock()
defer c.RUnlock()

View File

@@ -10,7 +10,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/bwmarrin/discordgo"
"github.com/matterbridge/discordgo"
)
const MessageLength = 1950
@@ -21,7 +21,6 @@ type Bdiscord struct {
c *discordgo.Session
nick string
useChannelID bool
guildID string
webhookID string
webhookToken string
@@ -72,9 +71,11 @@ func (b *Bdiscord) Connect() error {
}
b.Log.Info("Connection succeeded")
b.c.AddHandler(b.messageCreate)
b.c.AddHandler(b.messageTyping)
b.c.AddHandler(b.memberUpdate)
b.c.AddHandler(b.messageUpdate)
b.c.AddHandler(b.messageDelete)
b.c.AddHandler(b.messageDeleteBulk)
b.c.AddHandler(b.memberAdd)
b.c.AddHandler(b.memberRemove)
err = b.c.Open()
@@ -95,11 +96,11 @@ func (b *Bdiscord) Connect() error {
for _, guild := range guilds {
if guild.Name == serverName || guild.ID == serverName {
b.channels, err = b.c.GuildChannels(guild.ID)
b.guildID = guild.ID
guildFound = true
if err != nil {
break
}
b.guildID = guild.ID
guildFound = true
}
}
b.channelsMutex.Unlock()
@@ -113,30 +114,37 @@ func (b *Bdiscord) Connect() error {
b.Log.Infof("Server=\"%s\" # Server ID", guild.ID)
}
}
if err != nil {
return err
}
b.channelsMutex.RLock()
if b.GetString("WebhookURL") == "" {
for _, channel := range b.channels {
b.Log.Debugf("found channel %#v", channel)
}
} else {
b.canEditWebhooks = true
for _, channel := range b.channels {
b.Log.Debugf("found channel %#v; verifying PermissionManageWebhooks", channel)
perms, permsErr := b.c.State.UserChannelPermissions(userinfo.ID, channel.ID)
manageWebhooks := discordgo.PermissionManageWebhooks
if permsErr != nil || perms&manageWebhooks != manageWebhooks {
b.Log.Warnf("Can't manage webhooks in channel \"%s\"", channel.Name)
b.canEditWebhooks = false
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)
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
}
channelsDenied = append(channelsDenied, fmt.Sprintf("%#v", info.Name))
}
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, ", "))
}
}
b.channelsMutex.RUnlock()
@@ -172,10 +180,6 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
defer b.channelsMutex.Unlock()
b.channelInfoMap[channel.ID] = &channel
idcheck := strings.Split(channel.Name, "ID:")
if len(idcheck) > 1 {
b.useChannelID = true
}
return nil
}
@@ -187,6 +191,14 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
}
if msg.Event == config.EventUserTyping {
if b.GetBool("ShowUserTyping") {
err := b.c.ChannelTyping(channelID)
return "", err
}
return "", nil
}
// Make a action /me of the message
if msg.Event == config.EventUserAction {
msg.Text = "_" + msg.Text + "_"
@@ -208,26 +220,26 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
b.channelsMutex.RUnlock()
// Use webhook to send the message
if wID != "" {
if wID != "" && msg.Event != config.EventMsgDelete {
// skip events
if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
return "", nil
}
b.Log.Debugf("Broadcasting using Webhook")
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {
msg.Text += fi.Comment + ": "
}
if fi.URL != "" {
msg.Text = fi.URL
if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL
}
// If we are editing a message, delete the old message
if 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 == "" {
if msg.Text == "" && (msg.Extra == nil || len(msg.Extra["file"]) == 0) {
b.Log.Debugf("Skipping empty message %#v", msg)
return "", nil
}
@@ -250,16 +262,16 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
return "", err
}
}
err := b.c.WebhookExecute(
wID,
wToken,
true,
&discordgo.WebhookParams{
Content: msg.Text,
Username: msg.Username,
AvatarURL: msg.Avatar,
})
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
}
return msg.ID, nil
}
b.Log.Debugf("Broadcasting using token (API)")
@@ -301,7 +313,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
if err != nil {
return "", err
}
return res.ID, err
return res.ID, nil
}
// useWebhook returns true if we have a webhook defined somewhere
@@ -365,3 +377,83 @@ 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

@@ -2,20 +2,45 @@ package bdiscord
import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/bwmarrin/discordgo"
"github.com/matterbridge/discordgo"
)
func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam
rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete}
rmsg.Channel = b.getChannelName(m.ChannelID)
if b.useChannelID {
rmsg.Channel = "ID:" + m.ChannelID
}
b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
// TODO(qaisjp): if other bridges support bulk deletions, it could be fanned out centrally
func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageDeleteBulk) { //nolint:unparam
for _, msgID := range m.Messages {
rmsg := config.Message{
Account: b.Account,
ID: msgID,
Event: config.EventMsgDelete,
Text: config.EventMsgDelete,
Channel: b.getChannelName(m.ChannelID),
}
b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
}
func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) {
if !b.GetBool("ShowUserTyping") {
return
}
rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
rmsg.Channel = b.getChannelName(m.ChannelID)
b.Remote <- rmsg
}
func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam
if b.GetBool("EditDisable") {
return
@@ -24,7 +49,10 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat
if m.Message.EditedTimestamp != "" {
b.Log.Debugf("Sending edit message")
m.Content += b.GetString("EditSuffix")
b.messageCreate(s, (*discordgo.MessageCreate)(m))
msg := &discordgo.MessageCreate{
Message: m.Message,
}
b.messageCreate(s, msg)
}
}
@@ -36,7 +64,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 b.useWebhook() && m.Author.Bot && b.isWebhookID(m.Author.ID) {
return
}
@@ -51,7 +79,6 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
if m.Content != "" {
b.Log.Debugf("== Receiving event %#v", m.Message)
m.Message.Content = b.stripCustomoji(m.Message.Content)
m.Message.Content = b.replaceChannelMentions(m.Message.Content)
rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c)
if err != nil {
@@ -62,16 +89,13 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
// set channel name
rmsg.Channel = b.getChannelName(m.ChannelID)
if b.useChannelID {
rmsg.Channel = "ID:" + m.ChannelID
}
// set username
if !b.GetBool("UseUserName") {
rmsg.Username = b.getNick(m.Author)
fromWebhook := m.WebhookID != ""
if !fromWebhook && !b.GetBool("UseUserName") {
rmsg.Username = b.getNick(m.Author, m.GuildID)
} else {
rmsg.Username = m.Author.Username
if b.GetBool("UseDiscriminator") {
if !fromWebhook && b.GetBool("UseDiscriminator") {
rmsg.Username += "#" + m.Author.Discriminator
}
}
@@ -79,7 +103,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
// if we have embedded content add it to text
if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil {
for _, embed := range m.Message.Embeds {
rmsg.Text = rmsg.Text + "embed: " + embed.Title + " - " + embed.Description + " - " + embed.URL + "\n"
rmsg.Text += handleEmbed(embed)
}
}
@@ -168,3 +192,33 @@ func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRe
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
func handleEmbed(embed *discordgo.MessageEmbed) string {
var t []string
var result string
t = append(t, embed.Title)
t = append(t, embed.Description)
t = append(t, embed.URL)
i := 0
for _, e := range t {
if e == "" {
continue
}
i++
if i == 1 {
result += "embed: " + e
continue
}
result += " - " + e
}
if result != "" {
result += "\n"
}
return result
}

View File

@@ -0,0 +1,58 @@
package bdiscord
import (
"testing"
"github.com/matterbridge/discordgo"
"github.com/stretchr/testify/assert"
)
func TestHandleEmbed(t *testing.T) {
testcases := map[string]struct {
embed *discordgo.MessageEmbed
result string
}{
"allempty": {
embed: &discordgo.MessageEmbed{},
result: "",
},
"one": {
embed: &discordgo.MessageEmbed{
Title: "blah",
},
result: "embed: blah\n",
},
"two": {
embed: &discordgo.MessageEmbed{
Title: "blah",
Description: "blah2",
},
result: "embed: blah - blah2\n",
},
"three": {
embed: &discordgo.MessageEmbed{
Title: "blah",
Description: "blah2",
URL: "blah3",
},
result: "embed: blah - blah2 - blah3\n",
},
"twob": {
embed: &discordgo.MessageEmbed{
Description: "blah2",
URL: "blah3",
},
result: "embed: blah2 - blah3\n",
},
"oneb": {
embed: &discordgo.MessageEmbed{
URL: "blah3",
},
result: "embed: blah3\n",
},
}
for name, tc := range testcases {
assert.Equalf(t, tc.result, handleEmbed(tc.embed), "Testcases %s", name)
}
}

View File

@@ -6,10 +6,10 @@ import (
"strings"
"unicode"
"github.com/bwmarrin/discordgo"
"github.com/matterbridge/discordgo"
)
func (b *Bdiscord) getNick(user *discordgo.User) string {
func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string {
b.membersMutex.RLock()
defer b.membersMutex.RUnlock()
@@ -23,9 +23,9 @@ func (b *Bdiscord) getNick(user *discordgo.User) string {
}
// If we didn't find nick, search for it.
member, err := b.c.GuildMember(b.guildID, user.ID)
member, err := b.c.GuildMember(guildID, user.ID)
if err != nil {
b.Log.Warnf("Failed to fetch information for member %#v: %s", user, err)
b.Log.Warnf("Failed to fetch information for member %#v on guild %#v: %s", user, guildID, err)
return user.Username
} else if member == nil {
b.Log.Warnf("Got no information for member %#v", user)
@@ -51,6 +51,9 @@ func (b *Bdiscord) getGuildMemberByNick(nick string) (*discordgo.Member, error)
}
func (b *Bdiscord) getChannelID(name string) string {
if strings.Contains(name, "/") {
return b.getCategoryChannelID(name)
}
b.channelsMutex.RLock()
defer b.channelsMutex.RUnlock()
@@ -59,40 +62,91 @@ func (b *Bdiscord) getChannelID(name string) string {
return idcheck[1]
}
for _, channel := range b.channels {
if channel.Name == name {
if channel.Name == name && channel.Type == discordgo.ChannelTypeGuildText {
return channel.ID
}
}
return ""
}
func (b *Bdiscord) getCategoryChannelID(name string) string {
b.channelsMutex.RLock()
defer b.channelsMutex.RUnlock()
res := strings.Split(name, "/")
// shouldn't happen because function should be only called from getChannelID
if len(res) != 2 {
return ""
}
catName, chanName := res[0], res[1]
for _, channel := range b.channels {
// if we have a parentID, lookup the name of that parent (category)
// and if it matches return it
if channel.Name == chanName && channel.ParentID != "" {
for _, cat := range b.channels {
if cat.ID == channel.ParentID && cat.Name == catName {
return channel.ID
}
}
}
}
return ""
}
func (b *Bdiscord) getChannelName(id string) string {
b.channelsMutex.RLock()
defer b.channelsMutex.RUnlock()
for _, c := range b.channelInfoMap {
if c.Name == "ID:"+id {
// if we have ID: specified in our gateway configuration return this
return c.Name
}
}
for _, channel := range b.channels {
if channel.ID == id {
return channel.Name
return b.getCategoryChannelName(channel.Name, channel.ParentID)
}
}
return ""
}
func (b *Bdiscord) getCategoryChannelName(name, parentID string) string {
var usesCat bool
// do we have a category configuration in the channel config
for _, c := range b.channelInfoMap {
if strings.Contains(c.Name, "/") {
usesCat = true
break
}
}
// configuration without category, return the normal channel name
if !usesCat {
return name
}
// create a category/channel response
for _, c := range b.channels {
if c.ID == parentID {
name = c.Name + "/" + name
}
}
return name
}
var (
// See https://discordapp.com/developers/docs/reference#message-formatting.
channelMentionRE = regexp.MustCompile("<#[0-9]+>")
emojiRE = regexp.MustCompile("<(:.*?:)[0-9]+>")
userMentionRE = regexp.MustCompile("@[^@\n]{1,32}")
)
func (b *Bdiscord) replaceChannelMentions(text string) string {
replaceChannelMentionFunc := func(match string) string {
var err error
channelID := match[2 : len(match)-1]
channelName := b.getChannelName(channelID)
// If we don't have the channel refresh our list.
if channelName == "" {
var err error
b.channels, err = b.c.GuildChannels(b.guildID)
if err != nil {
return "#unknownchannel"
@@ -128,13 +182,9 @@ func (b *Bdiscord) replaceUserMentions(text string) string {
return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc)
}
func (b *Bdiscord) stripCustomoji(text string) string {
return emojiRE.ReplaceAllString(text, `$1`)
}
func (b *Bdiscord) replaceAction(text string) (string, bool) {
if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") {
return text[1:], true
return text[1 : len(text)-1], true
}
return text, false
}

View File

@@ -14,8 +14,10 @@ import (
"golang.org/x/image/webp"
"github.com/42wim/matterbridge/bridge/config"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
"github.com/sirupsen/logrus"
"gitlab.com/golang-commonmark/markdown"
)
// DownloadFile downloads the given non-authenticated URL.
@@ -176,9 +178,15 @@ func ClipMessage(text string, length int) string {
return text
}
// ParseMarkdown takes in an input string as markdown and parses it to html
func ParseMarkdown(input string) string {
md := markdown.New(markdown.XHTMLOutput(true), markdown.Breaks(true))
res := md.RenderToString([]byte(input))
extensions := parser.HardLineBreak | parser.NoIntraEmphasis
markdownParser := parser.NewWithExtensions(extensions)
renderer := html.NewRenderer(html.RendererOptions{
Flags: 0,
})
parsedMarkdown := markdown.ToHTML([]byte(input), markdownParser, renderer)
res := string(parsedMarkdown)
res = strings.TrimPrefix(res, "<p>")
res = strings.TrimSuffix(res, "</p>\n")
return res

View File

@@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"io/ioutil"
"regexp"
"strconv"
"strings"
"time"
@@ -91,12 +90,13 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
if b.GetBool("nosendjoinpart") {
return
}
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
// QUIT isn't channel bound, happens for all channels on the bridge
if event.Command == "QUIT" {
channel = ""
}
msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
if b.GetBool("verbosejoinpart") {
b.Log.Debugf("<= Sending verbose JOIN_LEAVE event from %s to gateway", b.Account)
msg = config.Message{Username: "system", Text: event.Source.Name + " (" + event.Source.Ident + "@" + event.Source.Host + ") " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
} else {
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
}
b.Log.Debugf("<= Message is %#v", msg)
b.Remote <- msg
return
@@ -160,7 +160,10 @@ func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) {
b.handleNickServ()
b.handleRunCommands()
// we are now fully connected
b.connected <- nil
// only send on first connection
if b.FirstConnection {
b.connected <- nil
}
}
func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
@@ -178,10 +181,6 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
// strip action, we made an event if it was an action
rmsg.Text += event.StripAction()
// strip IRC colors
re := regexp.MustCompile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
rmsg.Text = re.ReplaceAllString(rmsg.Text, "")
// start detecting the charset
mycharset := b.GetString("Charset")
if mycharset == "" {

View File

@@ -137,6 +137,7 @@ func (b *Birc) Send(msg config.Message) (string, error) {
// we can be in between reconnects #385
if !b.i.IsConnected() {
b.Log.Error("Not connected to server, dropping message")
return "", nil
}
// Execute a command
@@ -166,12 +167,8 @@ func (b *Birc) Send(msg config.Message) (string, error) {
return "", nil
}
b.Local <- config.Message{
Text: msgLines[i],
Username: msg.Username,
Channel: msg.Channel,
Event: msg.Event,
}
msg.Text = msgLines[i]
b.Local <- msg
}
return "", nil
}
@@ -231,7 +228,7 @@ func (b *Birc) getClient() (*girc.Client, error) {
// fix strict user handling of girc
user := b.GetString("Nick")
for !girc.IsValidUser(user) {
if len(user) == 1 {
if len(user) == 1 || len(user) == 0 {
user = "matterbridge"
break
}

View File

@@ -0,0 +1,59 @@
package bkeybase
import (
"strconv"
"github.com/42wim/matterbridge/bridge/config"
"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1"
)
func (b *Bkeybase) handleKeybase() {
sub, err := b.kbc.ListenForNewTextMessages()
if err != nil {
b.Log.Errorf("Error listening: %s", err.Error())
}
go func() {
for {
msg, err := sub.Read()
if err != nil {
b.Log.Errorf("failed to read message: %s", err.Error())
}
if msg.Message.Content.TypeName != "text" {
continue
}
if msg.Message.Sender.Username == b.kbc.GetUsername() {
continue
}
b.handleMessage(msg.Message)
}
}()
}
func (b *Bkeybase) handleMessage(msg chat1.MsgSummary) {
b.Log.Debugf("== Receiving event: %#v", msg)
if msg.Channel.TopicName != b.channel || msg.Channel.Name != b.team {
return
}
if msg.Sender.Username != b.kbc.GetUsername() {
// TODO download avatar
// Create our message
rmsg := config.Message{Username: msg.Sender.Username, Text: msg.Content.Text.Body, UserID: string(msg.Sender.Uid), Channel: msg.Channel.TopicName, ID: strconv.Itoa(int(msg.Id)), Account: b.Account}
// Text must be a string
if msg.Content.TypeName != "text" {
b.Log.Errorf("message is not text")
return
}
b.Log.Debugf("<= Sending message from %s on %s to gateway", msg.Sender.Username, msg.Channel.Name)
b.Remote <- rmsg
}
}

106
bridge/keybase/keybase.go Normal file
View File

@@ -0,0 +1,106 @@
package bkeybase
import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/keybase/go-keybase-chat-bot/kbchat"
)
// Bkeybase bridge structure
type Bkeybase struct {
kbc *kbchat.API
user string
channel string
team string
*bridge.Config
}
// New initializes Bkeybase object and sets team
func New(cfg *bridge.Config) bridge.Bridger {
b := &Bkeybase{Config: cfg}
b.team = b.Config.GetString("Team")
return b
}
// Connect starts keybase API and listener loop
func (b *Bkeybase) Connect() error {
var err error
b.Log.Infof("Connecting %s", b.GetString("Team"))
// use default keybase location (`keybase`)
b.kbc, err = kbchat.Start(kbchat.RunOptions{})
if err != nil {
return err
}
b.user = b.kbc.GetUsername()
b.Log.Info("Connection succeeded")
go b.handleKeybase()
return nil
}
// Disconnect doesn't do anything for now
func (b *Bkeybase) Disconnect() error {
return nil
}
// JoinChannel sets channel name in struct
func (b *Bkeybase) JoinChannel(channel config.ChannelInfo) error {
if _, err := b.kbc.JoinChannel(b.team, channel.Name); err != nil {
return err
}
b.channel = channel.Name
return nil
}
// Send receives bridge messages and sends them to Keybase chat room
func (b *Bkeybase) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
// Handle /me events
if msg.Event == config.EventUserAction {
msg.Text = "_" + msg.Text + "_"
}
// Delete message if we have an ID
// Delete message not supported by keybase go library yet
// Edit message if we have an ID
// kbchat lib does not support message editing yet
if len(msg.Extra["file"]) > 0 {
// Upload a file
dir, err := ioutil.TempDir("", "matterbridge")
if err != nil {
return "", err
}
defer os.RemoveAll(dir)
for _, f := range msg.Extra["file"] {
fname := f.(config.FileInfo).Name
fdata := *f.(config.FileInfo).Data
fcaption := f.(config.FileInfo).Comment
fpath := filepath.Join(dir, fname)
if err = ioutil.WriteFile(fpath, fdata, 0600); err != nil {
return "", err
}
_, _ = b.kbc.SendAttachmentByTeam(b.team, &b.channel, fpath, fcaption)
}
return "", nil
}
// Send regular message
text := msg.Username + msg.Text
resp, err := b.kbc.SendMessageByTeamName(b.team, &b.channel, text)
if err != nil {
return "", err
}
return strconv.Itoa(int(*resp.Result.MessageID)), err
}

View File

@@ -172,10 +172,15 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
return
}
// TODO download avatar
// Create our message
rmsg := config.Message{Username: ev.Sender[1:], Channel: channel, Account: b.Account, UserID: ev.Sender, ID: ev.ID}
rmsg := config.Message{
Username: ev.Sender[1:],
Channel: channel,
Account: b.Account,
UserID: ev.Sender,
ID: ev.ID,
Avatar: b.getAvatarURL(ev.Sender),
}
// Text must be a string
if rmsg.Text, ok = ev.Content["body"].(string); !ok {
@@ -291,7 +296,8 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
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") {
if !(strings.Contains(mtype, "image") || strings.Contains(mtype, "video") ||
strings.Contains(mtype, "application") || strings.Contains(mtype, "audio")) {
return
}
if fi.Comment != "" {
@@ -326,6 +332,18 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
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)))
if err != nil {
b.Log.Errorf("sendAudio failed: %#v", err)
}
}
b.Log.Debugf("result: %#v", res)
}
@@ -345,3 +363,15 @@ func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool {
}
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/")
url += "?width=37&height=37&method=crop"
return url
}

View File

@@ -66,6 +66,10 @@ func (b *Bmattermost) handleMatter() {
} else {
b.Log.Debugf("Choosing login/password based receiving")
}
// if for some reason we only want to sent stuff to mattermost but not receive, return
if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") != "" && b.GetString("Token") == "" && b.GetString("Login") == "" {
b.Log.Debugf("No WebhookBindAddress specified, only WebhookURL. You will not receive messages from mattermost, only sending is possible.")
}
go b.handleMatterClient(messages)
}
var ok bool

View File

@@ -70,6 +70,7 @@ func (b *Bmattermost) apiLogin() error {
b.mc.SetLogLevel("debug")
}
b.mc.SkipTLSVerify = b.GetBool("SkipTLSVerify")
b.mc.SkipVersionCheck = b.GetBool("SkipVersionCheck")
b.mc.NoTLS = b.GetBool("NoTLS")
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
err := b.mc.Login()
@@ -186,6 +187,12 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
return true
}
// Ignore non-post messages
if message.Post == nil {
b.Log.Debugf("ignoring nil message.Post: %#v", message)
return true
}
// Ignore messages sent from matterbridge
if message.Post.Props != nil {
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {

View File

@@ -121,6 +121,12 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
return msg.ID, b.mc.DeleteMessage(msg.ID)
}
// Handle prefix hint for unthreaded messages.
if msg.ParentID == "msg-parent-not-found" {
msg.ParentID = ""
msg.Text = fmt.Sprintf("[thread]: %s", msg.Text)
}
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {

101
bridge/msteams/handler.go Normal file
View File

@@ -0,0 +1,101 @@
package bmsteams
import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
msgraph "github.com/matterbridge/msgraph.go/beta"
)
func (b *Bmsteams) findFile(weburl string) (string, error) {
itemRB, err := b.gc.GetDriveItemByURL(b.ctx, weburl)
if err != nil {
return "", err
}
itemRB.Workbook().Worksheets()
b.gc.Workbooks()
item, err := itemRB.Request().Get(b.ctx)
if err != nil {
return "", err
}
if url, ok := item.GetAdditionalData("@microsoft.graph.downloadUrl"); ok {
return url.(string), nil
}
return "", nil
}
// handleDownloadFile handles file download
func (b *Bmsteams) handleDownloadFile(rmsg *config.Message, filename, weburl string) error {
realURL, err := b.findFile(weburl)
if err != nil {
return err
}
// Actually download the file.
data, err := helper.DownloadFile(realURL)
if err != nil {
return fmt.Errorf("download %s failed %#v", weburl, err)
}
// If a comment is attached to the file(s) it is in the 'Text' field of the teams messge event
// and should be added as comment to only one of the files. We reset the 'Text' field to ensure
// that the comment is not duplicated.
comment := rmsg.Text
rmsg.Text = ""
helper.HandleDownloadData(b.Log, rmsg, filename, comment, weburl, data, b.General)
return nil
}
func (b *Bmsteams) handleAttachments(rmsg *config.Message, msg msgraph.ChatMessage) {
for _, a := range msg.Attachments {
//remove the attachment tags from the text
rmsg.Text = attachRE.ReplaceAllString(rmsg.Text, "")
//handle a code snippet (code block)
if *a.ContentType == "application/vnd.microsoft.card.codesnippet" {
b.handleCodeSnippet(rmsg, a)
continue
}
//handle the download
err := b.handleDownloadFile(rmsg, *a.Name, *a.ContentURL)
if err != nil {
b.Log.Errorf("download of %s failed: %s", *a.Name, err)
}
}
}
type AttachContent struct {
Language string `json:"language"`
CodeSnippetURL string `json:"codeSnippetUrl"`
}
func (b *Bmsteams) handleCodeSnippet(rmsg *config.Message, attach msgraph.ChatMessageAttachment) {
var content AttachContent
err := json.Unmarshal([]byte(*attach.Content), &content)
if err != nil {
b.Log.Errorf("unmarshal codesnippet failed: %s", err)
return
}
s := strings.Split(content.CodeSnippetURL, "/")
if len(s) != 13 {
b.Log.Errorf("codesnippetUrl has unexpected size: %s", content.CodeSnippetURL)
return
}
resp, err := b.gc.Teams().Request().Client().Get(content.CodeSnippetURL)
if err != nil {
b.Log.Errorf("retrieving snippet content failed:%s", err)
return
}
defer resp.Body.Close()
res, err := ioutil.ReadAll(resp.Body)
if err != nil {
b.Log.Errorf("reading snippet data failed: %s", err)
return
}
rmsg.Text = rmsg.Text + "\n```" + content.Language + "\n" + string(res) + "\n```\n"
}

205
bridge/msteams/msteams.go Normal file
View File

@@ -0,0 +1,205 @@
package bmsteams
import (
"context"
"fmt"
"os"
"regexp"
"strings"
"time"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
// "github.com/davecgh/go-spew/spew"
msgraph "github.com/matterbridge/msgraph.go/beta"
"github.com/matterbridge/msgraph.go/msauth"
"github.com/mattn/godown"
"golang.org/x/oauth2"
)
var defaultScopes = []string{"openid", "profile", "offline_access", "Group.Read.All", "Group.ReadWrite.All"}
var attachRE = regexp.MustCompile(`<attachment id=.*?attachment>`)
type Bmsteams struct {
gc *msgraph.GraphServiceRequestBuilder
ctx context.Context
botID string
*bridge.Config
}
func New(cfg *bridge.Config) bridge.Bridger {
return &Bmsteams{Config: cfg}
}
func (b *Bmsteams) Connect() error {
tokenCachePath := b.GetString("sessionFile")
if tokenCachePath == "" {
tokenCachePath = "msteams_session.json"
}
ctx := context.Background()
m := msauth.NewManager()
m.LoadFile(tokenCachePath) //nolint:errcheck
ts, err := m.DeviceAuthorizationGrant(ctx, b.GetString("TenantID"), b.GetString("ClientID"), defaultScopes, nil)
if err != nil {
return err
}
err = m.SaveFile(tokenCachePath)
if err != nil {
b.Log.Errorf("Couldn't save sessionfile in %s: %s", tokenCachePath, err)
}
// make file readable only for matterbridge user
err = os.Chmod(tokenCachePath, 0600)
if err != nil {
b.Log.Errorf("Couldn't change permissions for %s: %s", tokenCachePath, err)
}
httpClient := oauth2.NewClient(ctx, ts)
graphClient := msgraph.NewClient(httpClient)
b.gc = graphClient
b.ctx = ctx
err = b.setBotID()
if err != nil {
return err
}
b.Log.Info("Connection succeeded")
return nil
}
func (b *Bmsteams) Disconnect() error {
return nil
}
func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error {
go b.poll(channel.Name)
return nil
}
func (b *Bmsteams) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
if msg.ParentID != "" && msg.ParentID != "msg-parent-not-found" {
return b.sendReply(msg)
}
if msg.ParentID == "msg-parent-not-found" {
msg.ParentID = ""
msg.Text = fmt.Sprintf("[thread]: %s", msg.Text)
}
ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().Request()
text := msg.Username + msg.Text
content := &msgraph.ItemBody{Content: &text}
rmsg := &msgraph.ChatMessage{Body: content}
res, err := ct.Add(b.ctx, rmsg)
if err != nil {
return "", err
}
return *res.ID, nil
}
func (b *Bmsteams) sendReply(msg config.Message) (string, error) {
ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(msg.Channel).Messages().ID(msg.ParentID).Replies().Request()
// Handle prefix hint for unthreaded messages.
text := msg.Username + msg.Text
content := &msgraph.ItemBody{Content: &text}
rmsg := &msgraph.ChatMessage{Body: content}
res, err := ct.Add(b.ctx, rmsg)
if err != nil {
return "", err
}
return *res.ID, nil
}
func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) {
ct := b.gc.Teams().ID(b.GetString("TeamID")).Channels().ID(channel).Messages().Request()
rct, err := ct.Get(b.ctx)
if err != nil {
return nil, err
}
b.Log.Debugf("got %#v messages", len(rct))
return rct, nil
}
//nolint:gocognit
func (b *Bmsteams) poll(channelName string) {
msgmap := make(map[string]time.Time)
b.Log.Debug("getting initial messages")
res, err := b.getMessages(channelName)
if err != nil {
panic(err)
}
for _, msg := range res {
msgmap[*msg.ID] = *msg.CreatedDateTime
if msg.LastModifiedDateTime != nil {
msgmap[*msg.ID] = *msg.LastModifiedDateTime
}
}
time.Sleep(time.Second * 5)
b.Log.Debug("polling for messages")
for {
res, err := b.getMessages(channelName)
if err != nil {
panic(err)
}
for i := len(res) - 1; i >= 0; i-- {
msg := res[i]
if mtime, ok := msgmap[*msg.ID]; ok {
if mtime == *msg.CreatedDateTime && msg.LastModifiedDateTime == nil {
continue
}
if msg.LastModifiedDateTime != nil && mtime == *msg.LastModifiedDateTime {
continue
}
}
if *msg.From.User.ID == b.botID {
b.Log.Debug("skipping own message")
msgmap[*msg.ID] = *msg.CreatedDateTime
continue
}
msgmap[*msg.ID] = *msg.CreatedDateTime
if msg.LastModifiedDateTime != nil {
msgmap[*msg.ID] = *msg.LastModifiedDateTime
}
b.Log.Debugf("<= Sending message from %s on %s to gateway", *msg.From.User.DisplayName, b.Account)
text := b.convertToMD(*msg.Body.Content)
rmsg := config.Message{
Username: *msg.From.User.DisplayName,
Text: text,
Channel: channelName,
Account: b.Account,
Avatar: "",
UserID: *msg.From.User.ID,
ID: *msg.ID,
Extra: make(map[string][]interface{}),
}
b.handleAttachments(&rmsg, msg)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
time.Sleep(time.Second * 5)
}
}
func (b *Bmsteams) setBotID() error {
req := b.gc.Me().Request()
r, err := req.Get(b.ctx)
if err != nil {
return err
}
b.botID = *r.ID
return nil
}
func (b *Bmsteams) convertToMD(text string) string {
if !strings.Contains(text, "<div>") {
return text
}
var sb strings.Builder
err := godown.Convert(&sb, strings.NewReader(text), nil)
if err != nil {
b.Log.Errorf("Couldn't convert message to markdown %s", text)
return text
}
return sb.String()
}

View File

@@ -58,6 +58,9 @@ func (b *Brocketchat) doConnectWebhookURL() error {
func (b *Brocketchat) apiLogin() error {
b.Log.Debugf("handling apiLogin()")
credentials := &models.UserCredentials{Email: b.GetString("login"), Password: b.GetString("password")}
if b.GetString("Token") != "" {
credentials = &models.UserCredentials{ID: b.GetString("Login"), Token: b.GetString("Token")}
}
myURL, err := url.Parse(b.GetString("server"))
if err != nil {
return err
@@ -95,7 +98,7 @@ func (b *Brocketchat) getChannelID(name string) string {
b.RLock()
defer b.RUnlock()
for k, v := range b.channelMap {
if v == name {
if v == name || v == "#"+name {
return k
}
}

View File

@@ -108,6 +108,11 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
msg.Channel = strings.TrimPrefix(msg.Channel, "#")
channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel}
// Make a action /me of the message
if msg.Event == config.EventUserAction {
msg.Text = "_" + msg.Text + "_"
}
// Delete message
if msg.Event == config.EventMsgDelete {
if msg.ID == "" {

View File

@@ -7,7 +7,7 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/nlopes/slack"
"github.com/slack-go/slack"
)
func (b *Bslack) handleSlack() {
@@ -22,20 +22,21 @@ func (b *Bslack) handleSlack() {
time.Sleep(time.Second)
b.Log.Debug("Start listening for Slack messages")
for message := range messages {
if message.Event != config.EventUserTyping {
// don't do any action on deleted/typing messages
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete {
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
// cleanup the message
message.Text = b.replaceMention(message.Text)
message.Text = b.replaceVariable(message.Text)
message.Text = b.replaceChannel(message.Text)
message.Text = b.replaceURL(message.Text)
message.Text = b.replaceb0rkedMarkDown(message.Text)
message.Text = html.UnescapeString(message.Text)
// Add the avatar
message.Avatar = b.users.getAvatar(message.UserID)
}
// cleanup the message
message.Text = b.replaceMention(message.Text)
message.Text = b.replaceVariable(message.Text)
message.Text = b.replaceChannel(message.Text)
message.Text = b.replaceURL(message.Text)
message.Text = html.UnescapeString(message.Text)
// Add the avatar
message.Avatar = b.users.getAvatar(message.UserID)
b.Log.Debugf("<= Message is %#v", message)
b.Remote <- *message
}
@@ -43,7 +44,7 @@ func (b *Bslack) handleSlack() {
func (b *Bslack) handleSlackClient(messages chan *config.Message) {
for msg := range b.rtm.IncomingEvents {
if msg.Type != sUserTyping && msg.Type != sLatencyReport {
if msg.Type != sUserTyping && msg.Type != sHello && msg.Type != sLatencyReport {
b.Log.Debugf("== Receiving event %#v", msg.Data)
}
switch ev := msg.Data.(type) {
@@ -86,7 +87,7 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
case *slack.MemberJoinedChannelEvent:
b.users.populateUser(ev.User)
case *slack.LatencyReport:
case *slack.HelloEvent, *slack.LatencyReport, *slack.ConnectingEvent:
continue
default:
b.Log.Debugf("Unhandled incoming event: %T", ev)
@@ -123,19 +124,31 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
}
}
// Check for our callback ID
hasOurCallbackID := false
if len(ev.Blocks.BlockSet) == 1 {
block, ok := ev.Blocks.BlockSet[0].(*slack.SectionBlock)
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
}
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
if ev.Username == sSlackBotUser ||
(b.rtm != nil && ev.Username == b.si.User.Name) ||
(len(ev.Attachments) > 0 && ev.Attachments[0].CallbackID == "matterbridge_"+b.uuid) {
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
return true
}
// It seems ev.SubMessage.Edited == nil when slack unfurls.
// Do not forward these messages. See Github issue #266.
if ev.SubMessage != nil &&
ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
ev.SubMessage.Edited == nil {
return true
if ev.SubMessage != nil {
// It seems ev.SubMessage.Edited == nil when slack unfurls.
// Do not forward these messages. See Github issue #266.
if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
ev.SubMessage.Edited == nil {
return true
}
// see hidden subtypes at https://api.slack.com/events/message
// these messages are sent when we add a message to a thread #709
if ev.SubType == "message_replied" && ev.Hidden {
return true
}
}
if len(ev.Files) > 0 {

View File

@@ -7,8 +7,8 @@ import (
"time"
"github.com/42wim/matterbridge/bridge/config"
"github.com/nlopes/slack"
"github.com/sirupsen/logrus"
"github.com/slack-go/slack"
)
// populateReceivedMessage shapes the initial Matterbridge message that we will forward to the
@@ -188,6 +188,36 @@ func (b *Bslack) replaceURL(text string) string {
return text
}
func (b *Bslack) replaceb0rkedMarkDown(text string) string {
// taken from https://github.com/mattermost/mattermost-server/blob/master/app/slackimport.go
//
regexReplaceAllString := []struct {
regex *regexp.Regexp
rpl string
}{
// bold
{
regexp.MustCompile(`(^|[\s.;,])\*(\S[^*\n]+)\*`),
"$1**$2**",
},
// strikethrough
{
regexp.MustCompile(`(^|[\s.;,])\~(\S[^~\n]+)\~`),
"$1~~$2~~",
},
// single paragraph blockquote
// Slack converts > character to &gt;
{
regexp.MustCompile(`(?sm)^&gt;`),
">",
},
}
for _, rule := range regexReplaceAllString {
text = rule.regex.ReplaceAllString(text, rule.rpl)
}
return text
}
func (b *Bslack) replaceCodeFence(text string) string {
return codeFenceRE.ReplaceAllString(text, "```")
}

View File

@@ -5,7 +5,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/matterhook"
"github.com/nlopes/slack"
"github.com/slack-go/slack"
)
type BLegacy struct {
@@ -13,7 +13,9 @@ type BLegacy struct {
}
func NewLegacy(cfg *bridge.Config) bridge.Bridger {
return &BLegacy{Bslack: newBridge(cfg)}
b := &BLegacy{Bslack: newBridge(cfg)}
b.legacy = true
return b
}
func (b *BLegacy) Connect() error {

View File

@@ -12,9 +12,9 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/42wim/matterbridge/matterhook"
"github.com/hashicorp/golang-lru"
"github.com/nlopes/slack"
lru "github.com/hashicorp/golang-lru"
"github.com/rs/xid"
"github.com/slack-go/slack"
)
type Bslack struct {
@@ -32,9 +32,11 @@ type Bslack struct {
channels *channels
users *users
legacy bool
}
const (
sHello = "hello"
sChannelJoin = "channel_join"
sChannelLeave = "channel_leave"
sChannelJoined = "channel_joined"
@@ -151,6 +153,18 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
return nil
}
// try to join a channel when in legacy
if b.legacy {
_, err := b.sc.JoinChannel(channel.Name)
if err != nil {
switch err.Error() {
case "name_taken", "restricted_action":
case "default":
return err
}
}
}
b.channels.populateChannels(false)
channelInfo, err := b.channels.getChannel(channel.Name)
@@ -163,7 +177,8 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
channel.Name = channelInfo.Name
}
if !channelInfo.IsMember {
// we can't join a channel unless we are using legacy tokens #651
if !channelInfo.IsMember && !b.legacy {
return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name)
}
return nil
@@ -393,7 +408,6 @@ func (b *Bslack) editMessage(msg *config.Message, channelInfo *slack.Channel) (b
}
messageOptions := b.prepareMessageOptions(msg)
for {
messageOptions = append(messageOptions, slack.MsgOptionText(msg.Text, false))
_, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...)
if err == nil {
return true, nil
@@ -412,11 +426,6 @@ func (b *Bslack) postMessage(msg *config.Message, channelInfo *slack.Channel) (s
return "", nil
}
messageOptions := b.prepareMessageOptions(msg)
messageOptions = append(
messageOptions,
slack.MsgOptionText(msg.Text, false),
slack.MsgOptionEnableLinkUnfurl(),
)
for {
_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...)
if err == nil {
@@ -482,8 +491,6 @@ func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption {
}
var attachments []slack.Attachment
// add a callback ID so we can see we created it
attachments = append(attachments, slack.Attachment{CallbackID: "matterbridge_" + b.uuid})
// add file attachments
attachments = append(attachments, b.createAttach(msg.Extra)...)
// add slack attachments (from another slack bridge)
@@ -494,6 +501,19 @@ func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption {
}
var opts []slack.MsgOption
opts = append(opts,
// provide regular text field (fallback used in Slack notifications, etc.)
slack.MsgOptionText(msg.Text, false),
// add a callback ID so we can see we created it
slack.MsgOptionBlocks(slack.NewSectionBlock(
slack.NewTextBlockObject(slack.MarkdownType, msg.Text, false, false),
nil, nil,
slack.SectionBlockOptionBlockID("matterbridge_"+b.uuid),
)),
slack.MsgOptionEnableLinkUnfurl(),
)
opts = append(opts, slack.MsgOptionAttachments(attachments...))
opts = append(opts, slack.MsgOptionPostMessageParameters(params))
return opts

View File

@@ -8,8 +8,8 @@ import (
"time"
"github.com/42wim/matterbridge/bridge/config"
"github.com/nlopes/slack"
"github.com/sirupsen/logrus"
"github.com/slack-go/slack"
)
const minimumRefreshInterval = 10 * time.Second
@@ -87,7 +87,11 @@ func (b *users) populateUser(userID string) {
// in case the previous query failed for some reason.
} else {
b.usersSyncPoints[userID] = make(chan struct{})
b.usersMutex.Unlock()
defer func() {
// Wake up any waiting goroutines and remove the synchronization point.
close(b.usersSyncPoints[userID])
delete(b.usersSyncPoints, userID)
}()
break
}
}
@@ -107,10 +111,6 @@ func (b *users) populateUser(userID string) {
// Register user information.
b.users[userID] = user
// Wake up any waiting goroutines and remove the synchronization point.
close(b.usersSyncPoints[userID])
delete(b.usersSyncPoints, userID)
}
func (b *users) populateUsers(wait bool) {

View File

@@ -130,6 +130,10 @@ func (b *Bsshchat) handleSSHChat() error {
if strings.Contains(b.r.Text(), "Rate limiting is in effect") {
continue
}
// skip our own messages
if !strings.HasPrefix(b.r.Text(), "["+b.GetString("Nick")+"] \x1b") {
continue
}
res := strings.Split(stripPrompt(b.r.Text()), ":")
if res[0] == "-> Set theme" {
wait = false

View File

@@ -85,7 +85,7 @@ func (b *Bsteam) handleEvents() {
func (b *Bsteam) handleLogOnFailed(e *steam.LogOnFailedEvent, myLoginInfo *steam.LogOnDetails) error {
switch e.Result {
case steamlang.EResult_AccountLogonDeniedNeedTwoFactorCode:
case steamlang.EResult_AccountLoginDeniedNeedTwoFactor:
b.Log.Info("Steam guard isn't letting me in! Enter 2FA code:")
var code string
fmt.Scanf("%s", &code)

View File

@@ -5,10 +5,11 @@ import (
"regexp"
"strconv"
"strings"
"unicode/utf16"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/go-telegram-bot-api/telegram-bot-api"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)
func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *tgbotapi.Message) *tgbotapi.Message {
@@ -94,7 +95,7 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
}
}
// only download avatars if we have a place to upload them (configured mediaserver)
if b.General.MediaServerUpload != "" {
if b.General.MediaServerUpload != "" || (b.General.MediaServerDownload != "" && b.General.MediaDownloadPath != "") {
b.handleDownloadAvatar(message.From.ID, rmsg.Channel)
}
}
@@ -125,6 +126,11 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
// handle groups
message = b.handleGroups(&rmsg, message, update)
if message == nil {
b.Log.Error("message is nil, this shouldn't happen.")
continue
}
// set the ID's from the channel or group message
rmsg.ID = strconv.Itoa(message.MessageID)
rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10)
@@ -351,6 +357,14 @@ func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string
if format == "" {
format = "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})"
}
quoteMessagelength := len(quoteMessage)
if b.GetInt("QuoteLengthLimit") != 0 && quoteMessagelength >= b.GetInt("QuoteLengthLimit") {
runes := []rune(quoteMessage)
quoteMessage = string(runes[0:b.GetInt("QuoteLengthLimit")])
if quoteMessagelength > b.GetInt("QuoteLengthLimit") {
quoteMessage += "..."
}
}
format = strings.Replace(format, "{MESSAGE}", message, -1)
format = strings.Replace(format, "{QUOTENICK}", quoteNick, -1)
format = strings.Replace(format, "{QUOTEMESSAGE}", quoteMessage, -1)
@@ -370,8 +384,13 @@ func (b *Btelegram) handleEntities(rmsg *config.Message, message *tgbotapi.Messa
b.Log.Errorf("entity text_link url parse failed: %s", err)
continue
}
link := rmsg.Text[e.Offset : e.Offset+e.Length]
rmsg.Text = strings.Replace(rmsg.Text, link, url.String(), 1)
utfEncodedString := utf16.Encode([]rune(rmsg.Text))
if e.Offset+e.Length > len(utfEncodedString) {
b.Log.Errorf("entity length is too long %d > %d", e.Offset+e.Length, len(utfEncodedString))
continue
}
link := utf16.Decode(utfEncodedString[e.Offset : e.Offset+e.Length])
rmsg.Text = strings.Replace(rmsg.Text, string(link), url.String(), 1)
}
}
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/go-telegram-bot-api/telegram-bot-api"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)
const (
@@ -81,8 +81,8 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
if _, err := b.sendMessage(chatid, rmsg.Username, rmsg.Text); err != nil {
b.Log.Errorf("sendMessage failed: %s", err)
if _, msgErr := b.sendMessage(chatid, rmsg.Username, rmsg.Text); msgErr != nil {
b.Log.Errorf("sendMessage failed: %s", msgErr)
}
}
// check if we have files to upload (from slack, telegram or mattermost)
@@ -97,7 +97,14 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
}
// Post normal message
return b.sendMessage(chatid, msg.Username, msg.Text)
// TODO: recheck it.
// Ignore empty text field needs for prevent double messages from whatsapp to telegram
// when sending media with text caption
if msg.Text != "" {
return b.sendMessage(chatid, msg.Username, msg.Text)
}
return "", nil
}
func (b *Btelegram) getFileDirectURL(id string) string {
@@ -124,6 +131,9 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, er
m.Text = username + html.EscapeString(text)
m.ParseMode = tgbotapi.ModeHTML
}
m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview")
res, err := b.c.Send(m)
if err != nil {
return "", err

View File

@@ -1,14 +1,15 @@
package bwhatsapp
import (
"fmt"
"mime"
"strings"
"time"
"github.com/42wim/matterbridge/bridge/config"
"github.com/matterbridge/go-whatsapp"
whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/Rhymen/go-whatsapp"
"github.com/jpillora/backoff"
)
/*
@@ -21,7 +22,42 @@ Check:
// HandleError received from WhatsApp
func (b *Bwhatsapp) HandleError(err error) {
b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
if strings.Contains(err.Error(), "error processing data: received invalid data") {
return
}
switch err.(type) {
case *whatsapp.ErrConnectionClosed, *whatsapp.ErrConnectionFailed:
b.reconnect(err)
default:
switch err {
case whatsapp.ErrConnectionTimeout:
b.reconnect(err)
default:
b.Log.Errorf("%v", err)
}
}
}
func (b *Bwhatsapp) reconnect(err error) {
bf := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
for {
d := bf.Duration()
b.Log.Errorf("Connection failed, underlying error: %v", err)
b.Log.Infof("Waiting %s...", d)
time.Sleep(d)
b.Log.Info("Reconnecting...")
err := b.conn.Restore()
if err == nil {
bf.Reset()
return
}
}
}
// HandleTextMessage sent from WhatsApp, relay it to the brige
@@ -35,16 +71,16 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
}
messageTime := time.Unix(int64(message.Info.Timestamp), 0) // TODO check how behaves between timezones
groupJid := message.Info.RemoteJid
groupJID := message.Info.RemoteJid
senderJid := message.Info.SenderJid
if len(senderJid) == 0 {
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
senderJID = *message.Info.Source.Participant
}
// translate sender's Jid to the nicest username we can get
senderName := b.getSenderName(senderJid)
// translate sender's JID to the nicest username we can get
senderName := b.getSenderName(senderJID)
if senderName == "" {
senderName = "Someone" // don't expose telephone number
}
@@ -52,12 +88,12 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
extText := message.Info.Source.Message.ExtendedTextMessage
if extText != nil && extText.ContextInfo != nil && extText.ContextInfo.MentionedJid != nil {
// handle user mentions
for _, mentionedJid := range extText.ContextInfo.MentionedJid {
numberAndSuffix := strings.SplitN(mentionedJid, "@", 2)
for _, mentionedJID := range extText.ContextInfo.MentionedJid {
numberAndSuffix := strings.SplitN(mentionedJID, "@", 2)
// mentions comes as telephone numbers and we don't want to expose it to other bridges
// replace it with something more meaninful to others
mention := b.getSenderNotify(numberAndSuffix[0] + whatsappExt.NewUserSuffix)
mention := b.getSenderNotify(numberAndSuffix[0] + "@s.whatsapp.net")
if mention == "" {
mention = "someone"
}
@@ -65,22 +101,22 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
}
}
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJid, b.Account)
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
rmsg := config.Message{
UserID: senderJid,
UserID: senderJID,
Username: senderName,
Text: message.Text,
Timestamp: messageTime,
Channel: groupJid,
Channel: groupJID,
Account: b.Account,
Protocol: b.Protocol,
Extra: make(map[string][]interface{}),
// ParentID: TODO, // TODO handle thread replies // map from Info.QuotedMessageID string
// 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}
if avatarURL, exists := b.userAvatars[senderJid]; exists {
if avatarURL, exists := b.userAvatars[senderJID]; exists {
rmsg.Avatar = avatarURL
}
@@ -88,11 +124,75 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
b.Remote <- rmsg
}
//
//func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) {
// fmt.Println(message) // TODO implement
//}
//
// 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") {
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
// }
// translate sender's Jid to the nicest username we can get
senderName := b.getSenderName(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}
if avatarURL, exists := b.userAvatars[senderJID]; exists {
rmsg.Avatar = avatarURL
}
// Download and unencrypt content
data, err := message.Download()
if err != nil {
b.Log.Errorf("%v", err)
return
}
// Get file extension by mimetype
fileExt, err := mime.ExtensionsByType(message.Type)
if err != nil {
b.Log.Errorf("%v", err)
return
}
filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0])
b.Log.Debugf("<= Image downloaded and unencrypted")
// 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.Remote <- rmsg
}
//func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) {
// fmt.Println(message) // TODO implement
//}

View File

@@ -2,13 +2,22 @@ package bwhatsapp
import (
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"os"
qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
"github.com/matterbridge/go-whatsapp"
"github.com/Rhymen/go-whatsapp"
)
type ProfilePicInfo struct {
URL string `json:"eurl"`
Tag string `json:"tag"`
Status int16 `json:"status"`
}
func qrFromTerminal(invert bool) chan string {
qr := make(chan string)
go func() {
@@ -82,3 +91,17 @@ func (b *Bwhatsapp) getSenderNotify(senderJid string) string {
}
return ""
}
func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) {
data, err := b.conn.GetProfilePicThumb(jid)
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
}

View File

@@ -1,20 +1,20 @@
package bwhatsapp
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"mime"
"os"
"path/filepath"
"strings"
"time"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/matterbridge/go-whatsapp"
whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext"
"github.com/Rhymen/go-whatsapp"
)
const (
@@ -29,10 +29,8 @@ type Bwhatsapp struct {
*bridge.Config
// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21
session *whatsapp.Session
conn *whatsapp.Conn
// https://github.com/tulir/mautrix-whatsapp/blob/master/whatsapp-ext/whatsapp.go
connExt *whatsappExt.ExtendedConn
session *whatsapp.Session
conn *whatsapp.Conn
startedAt uint64
users map[string]whatsapp.Contact
@@ -74,8 +72,6 @@ func (b *Bwhatsapp) Connect() error {
}
b.conn = conn
b.connExt = whatsappExt.ExtendConn(b.conn)
// TODO do we want to use it? b.connExt.SetClientName("Matterbridge WhatsApp bridge", "mb-wa")
b.conn.AddHandler(b)
b.Log.Debugln("WhatsApp connection successful")
@@ -89,7 +85,7 @@ func (b *Bwhatsapp) Connect() error {
b.Log.Debugln("Restoring WhatsApp session..")
// https://github.com/Rhymen/go-whatsapp#restore
session, err = b.conn.RestoreSession(session)
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)
@@ -130,7 +126,7 @@ func (b *Bwhatsapp) Connect() error {
b.Log.Debug("Getting user avatars..")
for jid := range b.users {
info, err := b.connExt.GetProfilePicThumb(jid)
info, err := b.GetProfilePicThumb(jid)
if err != nil {
b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
@@ -237,6 +233,66 @@ func (b *Bwhatsapp) JoinChannel(channel config.ChannelInfo) error {
return nil
}
// Post a document message from the bridge to WhatsApp
func (b *Bwhatsapp) PostDocumentMessage(msg config.Message, filetype string) (string, error) {
fi := msg.Extra["file"][0].(config.FileInfo)
// Post document message
message := whatsapp.DocumentMessage{
Info: whatsapp.MessageInfo{
RemoteJid: msg.Channel,
},
Title: fi.Name,
FileName: fi.Name,
Type: filetype,
Content: bytes.NewReader(*fi.Data),
}
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
}
// Post an image message from the bridge to WhatsApp
// Handle, for sure image/jpeg, image/png and image/gif MIME types
func (b *Bwhatsapp) PostImageMessage(msg config.Message, filetype string) (string, error) {
fi := msg.Extra["file"][0].(config.FileInfo)
// Post image message
message := whatsapp.ImageMessage{
Info: whatsapp.MessageInfo{
RemoteJid: msg.Channel,
},
Type: filetype,
Caption: msg.Username + fi.Comment,
Content: bytes.NewReader(*fi.Data),
}
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
}
// Send a message from the bridge to WhatsApp
// Required implementation of the Bridger interface
// https://github.com/42wim/matterbridge/blob/2cfd880cdb0df29771bf8f31df8d990ab897889d/bridge/bridge.go#L11-L16
@@ -266,18 +322,25 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
// TODO handle edit as a message reply with updated text
}
//// TODO Handle Upload a file
//if msg.Extra != nil {
// for _, rmsg := range helper.HandleExtra(&msg, b.General) {
// b.c.SendMessage(roomID, rmsg.Username+rmsg.Text)
// }
// if len(msg.Extra["file"]) > 0 {
// return b.handleUploadFile(&msg, roomID)
// }
//}
// Handle Upload a file
if msg.Extra["file"] != nil {
fi := msg.Extra["file"][0].(config.FileInfo)
filetype := mime.TypeByExtension(filepath.Ext(fi.Name))
b.Log.Debugf("Extra file is %#v", filetype)
// TODO: add different types
// TODO: add webp conversion
switch filetype {
case "image/jpeg", "image/png", "image/gif":
return b.PostImageMessage(msg, filetype)
default:
return b.PostDocumentMessage(msg, filetype)
}
}
// Post text message
text := whatsapp.TextMessage{
message := whatsapp.TextMessage{
Info: whatsapp.MessageInfo{
RemoteJid: msg.Channel, // which equals to group id
},
@@ -288,15 +351,14 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
// create message ID
// TODO follow and act if https://github.com/Rhymen/go-whatsapp/issues/101 implemented
bytes := make([]byte, 10)
if _, err := rand.Read(bytes); err != nil {
idBytes := make([]byte, 10)
if _, err := rand.Read(idBytes); err != nil {
b.Log.Warn(err.Error())
}
text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes))
message.Info.Id = strings.ToUpper(hex.EncodeToString(idBytes))
_, err := b.conn.Send(message)
err := b.conn.Send(text)
return text.Info.Id, err
return message.Info.Id, err
}
// 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

@@ -2,7 +2,9 @@ package bxmpp
import (
"crypto/tls"
"fmt"
"strings"
"sync"
"time"
"github.com/42wim/matterbridge/bridge"
@@ -14,50 +16,31 @@ import (
)
type Bxmpp struct {
xc *xmpp.Client
xmppMap map[string]string
*bridge.Config
startTime time.Time
xc *xmpp.Client
xmppMap map[string]string
connected bool
sync.RWMutex
}
func New(cfg *bridge.Config) bridge.Bridger {
b := &Bxmpp{Config: cfg}
b.xmppMap = make(map[string]string)
return b
return &Bxmpp{
Config: cfg,
xmppMap: make(map[string]string),
}
}
func (b *Bxmpp) Connect() error {
var err error
b.Log.Infof("Connecting %s", b.GetString("Server"))
b.xc, err = b.createXMPP()
if err != nil {
if err := b.createXMPP(); err != nil {
b.Log.Debugf("%#v", err)
return err
}
b.Log.Info("Connection succeeded")
go func() {
initial := true
bf := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
for {
if initial {
b.handleXMPP()
initial = false
}
d := bf.Duration()
b.Log.Infof("Disconnected. Reconnecting in %s", d)
time.Sleep(d)
b.xc, err = b.createXMPP()
if err == nil {
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EventRejoinChannels}
b.handleXMPP()
bf.Reset()
}
}
}()
go b.manageConnection()
return nil
}
@@ -76,40 +59,61 @@ func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
}
func (b *Bxmpp) Send(msg config.Message) (string, error) {
// should be fixed by using a cache instead of dropping
if !b.Connected() {
return "", fmt.Errorf("bridge %s not connected, dropping message %#v to bridge", b.Account, msg)
}
// ignore delete messages
if msg.Event == config.EventMsgDelete {
return "", nil
}
b.Log.Debugf("=> Receiving %#v", msg)
// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)
// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})
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 {
b.Log.WithError(err).Error("Unable to send message with share URL.")
}
}
if len(msg.Extra["file"]) > 0 {
return b.handleUploadFile(&msg)
return "", b.handleUploadFile(&msg)
}
}
var msgreplaceid string
msgid := xid.New().String()
var msgReplaceID string
msgID := xid.New().String()
if msg.ID != "" {
msgid = msg.ID
msgreplaceid = msg.ID
msgID = msg.ID
msgReplaceID = msg.ID
}
// Post normal message
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text, ID: msgid, ReplaceID: msgreplaceid})
if err != nil {
// Post normal message.
b.Log.Debugf("=> Sending message %#v", msg)
if _, err := b.xc.Send(xmpp.Chat{
Type: "groupchat",
Remote: msg.Channel + "@" + b.GetString("Muc"),
Text: msg.Username + msg.Text,
ID: msgID,
ReplaceID: msgReplaceID,
}); err != nil {
return "", err
}
return msgid, nil
return msgID, nil
}
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
tc := new(tls.Config)
tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify")
tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]
func (b *Bxmpp) createXMPP() error {
if !strings.Contains(b.GetString("Jid"), "@") {
return fmt.Errorf("the Jid %s doesn't contain an @", b.GetString("Jid"))
}
tc := &tls.Config{
ServerName: strings.Split(b.GetString("Jid"), "@")[1],
InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
}
options := xmpp.Options{
Host: b.GetString("Server"),
User: b.GetString("Jid"),
@@ -127,7 +131,54 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
}
var err error
b.xc, err = options.NewClient()
return b.xc, err
return err
}
func (b *Bxmpp) manageConnection() {
b.setConnected(true)
initial := true
bf := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
// Main connection loop. Each iteration corresponds to a successful
// connection attempt and the subsequent handling of the connection.
for {
if initial {
initial = false
} else {
b.Remote <- config.Message{
Username: "system",
Text: "rejoin",
Channel: "",
Account: b.Account,
Event: config.EventRejoinChannels,
}
}
if err := b.handleXMPP(); err != nil {
b.Log.WithError(err).Error("Disconnected.")
b.setConnected(false)
}
// Reconnection loop using an exponential back-off strategy. We
// only break out of the loop if we have successfully reconnected.
for {
d := bf.Duration()
b.Log.Infof("Reconnecting in %s.", d)
time.Sleep(d)
b.Log.Infof("Reconnecting now.")
if err := b.createXMPP(); err == nil {
b.setConnected(true)
bf.Reset()
break
}
b.Log.Warn("Failed to reconnect.")
}
}
}
func (b *Bxmpp) xmppKeepAlive() chan bool {
@@ -139,8 +190,7 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
select {
case <-ticker.C:
b.Log.Debugf("PING")
err := b.xc.PingC2S("", "")
if err != nil {
if err := b.xc.PingC2S("", ""); err != nil {
b.Log.Debugf("PING failed %#v", err)
}
case <-done:
@@ -152,31 +202,35 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
}
func (b *Bxmpp) handleXMPP() error {
var ok bool
var msgid string
b.startTime = time.Now()
done := b.xmppKeepAlive()
defer close(done)
for {
m, err := b.xc.Recv()
if err != nil {
return err
}
switch v := m.(type) {
case xmpp.Chat:
if v.Type == "groupchat" {
b.Log.Debugf("== Receiving %#v", v)
event := ""
// skip invalid messages
// Skip invalid messages.
if b.skipMessage(v) {
continue
}
var event string
if strings.Contains(v.Text, "has set the subject to:") {
event = config.EventTopicChange
}
msgid = v.ID
msgID := v.ID
if v.ReplaceID != "" {
msgid = v.ReplaceID
msgID = v.ReplaceID
}
rmsg := config.Message{
Username: b.parseNick(v.Remote),
@@ -184,21 +238,23 @@ func (b *Bxmpp) handleXMPP() error {
Channel: b.parseChannel(v.Remote),
Account: b.Account,
UserID: v.Remote,
ID: msgid,
ID: msgID,
Event: event,
}
// check if we have an action event
// Check if we have an action event.
var ok bool
rmsg.Text, ok = b.replaceAction(rmsg.Text)
if ok {
rmsg.Event = config.EventUserAction
}
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
}
case xmpp.Presence:
// do nothing
// Do nothing.
}
}
}
@@ -211,30 +267,41 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) {
}
// handleUploadFile handles native upload of files
func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
var urldesc = ""
func (b *Bxmpp) handleUploadFile(msg *config.Message) error {
var urlDesc string
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {
msg.Text += fi.Comment + ": "
for _, file := range msg.Extra["file"] {
fileInfo := file.(config.FileInfo)
if fileInfo.Comment != "" {
msg.Text += fileInfo.Comment + ": "
}
if fi.URL != "" {
msg.Text = fi.URL
if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL
urldesc = fi.Comment
if fileInfo.URL != "" {
msg.Text = fileInfo.URL
if fileInfo.Comment != "" {
msg.Text = fileInfo.Comment + ": " + fileInfo.URL
urlDesc = fileInfo.Comment
}
}
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
if err != nil {
return "", err
if _, err := b.xc.Send(xmpp.Chat{
Type: "groupchat",
Remote: msg.Channel + "@" + b.GetString("Muc"),
Text: msg.Username + msg.Text,
}); err != nil {
return err
}
if fi.URL != "" {
b.xc.SendOOB(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Ooburl: fi.URL, Oobdesc: urldesc})
if fileInfo.URL != "" {
if _, err := b.xc.SendOOB(xmpp.Chat{
Type: "groupchat",
Remote: msg.Channel + "@" + b.GetString("Muc"),
Ooburl: fileInfo.URL,
Oobdesc: urlDesc,
}); err != nil {
b.Log.WithError(err).Warn("Failed to send share URL.")
}
}
}
return "", nil
return nil
}
func (b *Bxmpp) parseNick(remote string) string {
@@ -279,6 +346,17 @@ func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
}
// skip delayed messages
t := time.Time{}
return message.Stamp != t
return !message.Stamp.IsZero() && time.Since(message.Stamp).Minutes() > 5
}
func (b *Bxmpp) setConnected(state bool) {
b.Lock()
b.connected = state
defer b.Unlock()
}
func (b *Bxmpp) Connected() bool {
b.RLock()
defer b.RUnlock()
return b.connected
}

View File

@@ -1,3 +1,220 @@
# v1.17.0
## New features
- msteams: new protocol added. Add initial Microsoft Teams support #967
See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup for a complete walkthrough
- discord: Add ability to procure avatars from the destination bridge #1000
- matrix: Add support for avatars from matrix. #1007
- general: support JSON and YAML config formats #1045
## Enhancements
- discord: Check only bridged channels for PermManageWebhooks #1001
- irc: Be less lossy when throttling IRC messages #1004
- keybase: updated library #1002, #1019
- matrix: Rebase gomatrix vendor with upstream #1006
- slack: Use upstream slack-go/slack again #1018
- slack: Ignore ConnectingEvent #1041
- slack: use blocks not attachments #1048
- sshchat: Update vendor shazow/ssh-chat #1029
- telegram: added markdownv2 mode for telegram #1037
- whatsapp: Implement basic reconnect (whatsapp). Fixes #987 #1003
## Bugfix
- discord: Fix webhook permission checks sometimes failing #1043
- discord: Fix #1027: warning when handling inbound webhooks #1044
- discord: Fix duplicate separator on empty description/url (discord) #1035
- matrix: Fix issue with underscores in links #999
- slack: Fix #1039: messages sent to Slack being synced back #1046
- telegram: Make avatars download work with mediaserverdownload (telegram). Fixes #920
This release couldn't exist without the following contributors:
@qaisjp, @jakubgs, @burner1024, @notpushkin, @MartijnBraam, @42wim
# v1.16.5
- Fix version bump
# v1.16.4
## New features
- whatsapp: Add support for WhatsApp media (jpeg/png/gif) bridging (#974)
- telegram: Add QuoteLengthLimit option (telegram) fixes #963 (#985)
- telegram: Add DisableWebPagePreview option (telegram). Closes #980 (#994)
## Enhancements
- general: update dependencies
- tengo: update to tengo v2
- general: Add Docker Compose configuration (#990)
## Bugfix
- general: Fail with message instead of panic. #988 (#991)
- telegram: Add extra mimetypes to docker image. Fixes #969
- discord: Fix channel ID problem with multiple gateways (discord). Fixes #953 (#977)
- discord: Show file comment in webhook if normal message is empty (discord). Fixes #962 (#995)
- matrix: Fix parsing issues - Disable smartypants in markdown parser. Fixes #989, #983 (#993)
- sshchat: Fix duplicated messages (sshchat). Fixes #950 (#996)
This release couldn't exist without the following contributors:
@jwflory, @42wim, @pbek, @Humorhenker, @c0ncord2, @glazzara
# v1.16.3
## Bugfix
- slack: Fix issues with ratelimiting #959
- mattermost: Fix bug when using webhookURL and login/token together #960
# v1.16.2
## New features
- keybase: Add support for receiving attachments (keybase) (#923)
## Enhancements
- general: Switch to new emoji library kyokomi/emoji (#948)
- general: Update markdown parsing library to github.com/gomarkdown/markdown (#944)
- ssh-chat: Update shazow/ssh-chat dependency (#947)
## Bugfix
- slack: Fix issues with the slack block kit API #937 (#943).
This release couldn't exist without the following contributors:
@42wim, @bmpickford, @goncalor
# v1.16.1
## New features
* rocketchat: add token support #892
* matrix: Add support for uploading application/x and audio/x (matrix). #929
## Enhancements
* general: Do configuration validation on start-up. Fixes #888
* general: updated vendored libraries (discord/whatsapp) #932
* discord: user typing messages #914
* slack: Convert slack bold/strike to correct markdown (slack). Fixes #918
## Bugfix
* discord: fix Failed to fetch information for members message. #894
* discord: remove obsolete file upload links (discord). #931
* slack: suppress unhandled HelloEvent message #913
* mattermost: Fix panic on WebhookURL only setting (mattermost). #917
* matrix: fix corrupted links between slack and matrix #924
This release couldn't exist without the following contributors:
@qaisjp, @hramrach, @42wim
# v1.16.0
## New features
* keybase: new protocol added. Add initial Keybase Chat support #877 Thanks to @hyperobject
* discord: Support webhook files in discord #872
## Enhancements
* general: update dependencies
## Bugfix
* discord: Underscores from Discord don't arrive correctly #864
* xmpp: Fix possible panic at startup of the XMPP bridge #869
* mattermost: Make getChannelIdTeam behave like GetChannelId for groups (mattermost) #873
This release couldn't exist without the following contributors:
@hyperobject, @42wim, @bucko909, @MOZGIII
# v1.15.1
## New features
* discord: Support webhook message deletions (discord) (#853)
## Enhancements
* discord: Support bulk deletions #851
* discord: Support channels in categories #863 (use category/channel. See matterbridge.toml.sample for more info)
* mattermost: Add an option to skip the Mattermost server version check #849
## Bugfix
* xmpp: fix segfault when disconnected/reconnected #856
* telegram: fix panic in handleEntities #858
This release couldn't exist without the following contributors:
@42wim, @qaisjp, @joohoi
# v1.15.0
## New features
* Add scripting (tengo) support for every outgoing message (#806)
See https://github.com/42wim/matterbridge/wiki/Settings#tengo and
https://github.com/42wim/matterbridge/wiki/Settings#outmessage for more information
* Add tengo support to RemoteNickFormat (#793)
See https://github.com/42wim/matterbridge/wiki/Settings#remotenickformat-2
* Deprecated `Message` under `[tengo]` to `InMessage`
## Enhancements
* general: Forward only user-typing messages if supported by protocol (#832)
* general: updated wiki with all possible settings: https://github.com/42wim/matterbridge/wiki/Settings
* tengo: Add msg event to tengo
* xmpp: Verify TLS against JID domain, not the host. (xmpp) (#834)
* xmpp: Allow messages with timestamp (xmpp). Fixes #835 (#847)
* irc: Add verbose IRC joins/parts (ident@host) (#805)
See https://github.com/42wim/matterbridge/wiki/Settings#verbosejoinpart
* rocketchat: Add useraction support (rocketchat). Closes #772 (#794)
## Bugfix
* slack: Fix regression in autojoining with legacy tokens (slack). Fixes #651 (#848)
* xmpp: Revert xmpp to orig behaviour. Closes #844
* whatsapp: Update github.com/Rhymen/go-whatsapp vendor. Fixes #843
* mattermost: Update channels of all teams (mattermost)
This release couldn't exist without the following contributors:
@42wim, @Helcaraxan, @chotaire, @qaisjp, @dajohi, @kousu
# v1.14.4
## Bugfix
* mattermost: Add Id to EditMessage (mattermost). Fixes #802
* mattermost: Fix panic on nil message.Post (mattermost). Fixes #804
* mattermost: Handle unthreaded messages (mattermost). Fixes #803
* mattermost: Use paging in initUser and UpdateUsers (mattermost)
* slack: Add lacking clean-up in Slack synchronisation (#811)
* slack: Disable user lookups on delete messages (slack) (#812)
# v1.14.3
## Bugfix
* irc: Fix deadlock on reconnect (irc). Closes #757
# v1.14.2
## Bugfix
* general: Update tengo vendor and load the stdlib. Fixes #789 (#792)
* rocketchat: Look up #channel too (rocketchat). Fix #773 (#775)
* slack: Ignore messagereplied and hidden messages (slack). Fixes #709 (#779)
* telegram: Handle nil message (telegram). Fixes #777
* irc: Use default nick if none specified (irc). Fixes #785
* irc: Return when not connected and drop a message (irc). Fixes #786
* irc: Revert fix for #722 (Support quits from irc correctly). Closes #781
## Contributors
This release couldn't exist without the following contributors:
@42wim, @Helcaraxan, @dajohi
# v1.14.1
## Bugfix
* slack: Fix crash double unlock (slack) (#771)
# v1.14.0
## Breaking

View File

@@ -1,5 +1,8 @@
#!/bin/bash
go version | grep go1.11 || exit
#!/usr/bin/env bash
set -u -e -x -o pipefail
go version | grep go1.14 || exit
VERSION=$(git describe --tags)
mkdir ci/binaries
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe

17
ci/lint.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -u -e -x -o pipefail
if [[ -n "${GOLANGCI_VERSION-}" ]]; then
# Retrieve the golangci-lint linter binary.
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION}
fi
# Run the linter.
golangci-lint run
# if [[ "${GO111MODULE-off}" == "on" ]]; then
# # If Go modules are active then check that dependencies are correctly maintained.
# go mod tidy
# go mod vendor
# git diff --exit-code --quiet || (echo "Please run 'go mod tidy' to clean up the 'go.mod' and 'go.sum' files."; false)
# fi

17
ci/test.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -u -e -x -o pipefail
if [[ -n "${REPORT_COVERAGE+cover}" ]]; then
# Retrieve and prepare CodeClimate's test coverage reporter.
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
./cc-test-reporter before-build
fi
# Run all the tests with the race detector and generate coverage.
go test -v -race -coverprofile c.out ./...
if [[ -n "${REPORT_COVERAGE+cover}" && "${TRAVIS_SECURE_ENV_VARS}" == "true" ]]; then
# Upload test coverage to CodeClimate.
./cc-test-reporter after-build
fi

View File

@@ -0,0 +1,10 @@
text := import("text")
// if we're not sending to a discord bridge,
// then convert custom emoji tags into url's
if (inProtocol == "discord" && outProtocol != "discord") {
rePNG := text.re_compile(`<:.*?:([0-9]+)>`)
msgText=rePNG.replace(msgText,"https://cdn.discordapp.com/emojis/$1.png")
reGIF := text.re_compile(`<a:.*?:([0-9]+)>`)
msgText=reGIF.replace(msgText,"https://cdn.discordapp.com/emojis/$1.gif")
}

View File

@@ -0,0 +1,14 @@
// See https://github.com/42wim/matterbridge/issues/881
// Generates a colored nick for each msgUsername, with example to filter specific codes
text := import("text")
fmt := import("fmt")
if outProtocol == "irc" {
// generate a color for a nick, make sure it isn't 0 or 15
colorCode := len(msgUsername)+bytes(msgUsername)[0]%14 + 2
// example if we want to use colorCode 3 when we have calculated colorcode 14
if colorCode == 14 {
colorCode = 3
}
msgUsername=fmt.sprintf("\x03%02d%s\x0F", colorCode, msgUsername)
}

View File

@@ -0,0 +1,7 @@
// See https://github.com/42wim/matterbridge/issues/798
// if we're not sending to an irc bridge we strip the IRC colors
if outProtocol != "irc" {
re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
msgText=re.replace(msgText,"")
}

View File

@@ -0,0 +1,16 @@
/*
This script will return the nick except with multi-character usernames
containing a zero-width space between the first and second character letter.
Single character usernames will be left untouched.
This is useful to prevent remote users from nickalerting
IRC users of the same name when the remote user speaks.
This result can be used in {TENGO} in RemoteNickFormat.
*/
result = nick
if len(nick) > 1 {
result = string(nick[0]) + "" + nick[1:]
}

View File

@@ -0,0 +1,9 @@
/*
This script will return the current time in kitchen format if the protocol (of the remote bridge) isn't irc
See https://github.com/d5/tengo/blob/master/docs/stdlib-times.md
This result can be used in {TENGO} in RemoteNickFormat
*/
times := import("times")
if protocol != "irc" {
result=times.time_format(times.now(),times.format_kitchen)
}

View File

@@ -3,35 +3,46 @@ package bridgemap
import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/api"
"github.com/42wim/matterbridge/bridge/discord"
"github.com/42wim/matterbridge/bridge/gitter"
"github.com/42wim/matterbridge/bridge/irc"
"github.com/42wim/matterbridge/bridge/matrix"
"github.com/42wim/matterbridge/bridge/mattermost"
"github.com/42wim/matterbridge/bridge/rocketchat"
"github.com/42wim/matterbridge/bridge/slack"
"github.com/42wim/matterbridge/bridge/sshchat"
"github.com/42wim/matterbridge/bridge/steam"
"github.com/42wim/matterbridge/bridge/telegram"
"github.com/42wim/matterbridge/bridge/whatsapp"
"github.com/42wim/matterbridge/bridge/xmpp"
"github.com/42wim/matterbridge/bridge/zulip"
bdiscord "github.com/42wim/matterbridge/bridge/discord"
bgitter "github.com/42wim/matterbridge/bridge/gitter"
birc "github.com/42wim/matterbridge/bridge/irc"
bkeybase "github.com/42wim/matterbridge/bridge/keybase"
bmatrix "github.com/42wim/matterbridge/bridge/matrix"
bmattermost "github.com/42wim/matterbridge/bridge/mattermost"
bmsteams "github.com/42wim/matterbridge/bridge/msteams"
brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
bslack "github.com/42wim/matterbridge/bridge/slack"
bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
bsteam "github.com/42wim/matterbridge/bridge/steam"
btelegram "github.com/42wim/matterbridge/bridge/telegram"
bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
bzulip "github.com/42wim/matterbridge/bridge/zulip"
)
var FullMap = map[string]bridge.Factory{
"api": api.New,
"discord": bdiscord.New,
"gitter": bgitter.New,
"irc": birc.New,
"mattermost": bmattermost.New,
"matrix": bmatrix.New,
"rocketchat": brocketchat.New,
"slack-legacy": bslack.NewLegacy,
"slack": bslack.New,
"sshchat": bsshchat.New,
"steam": bsteam.New,
"telegram": btelegram.New,
"whatsapp": bwhatsapp.New,
"xmpp": bxmpp.New,
"zulip": bzulip.New,
}
var (
FullMap = map[string]bridge.Factory{
"api": api.New,
"discord": bdiscord.New,
"gitter": bgitter.New,
"irc": birc.New,
"mattermost": bmattermost.New,
"matrix": bmatrix.New,
"rocketchat": brocketchat.New,
"slack-legacy": bslack.NewLegacy,
"slack": bslack.New,
"sshchat": bsshchat.New,
"steam": bsteam.New,
"telegram": btelegram.New,
"whatsapp": bwhatsapp.New,
"xmpp": bxmpp.New,
"zulip": bzulip.New,
"keybase": bkeybase.New,
"msteams": bmsteams.New,
}
UserTypingSupport = map[string]struct{}{
"slack": {},
"discord": {},
}
)

View File

@@ -9,9 +9,11 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/d5/tengo/script"
"github.com/42wim/matterbridge/internal"
"github.com/d5/tengo/v2"
"github.com/d5/tengo/v2/stdlib"
lru "github.com/hashicorp/golang-lru"
"github.com/peterhellberg/emojilib"
"github.com/matterbridge/emoji"
"github.com/sirupsen/logrus"
)
@@ -83,6 +85,7 @@ func (gw *Gateway) FindCanonicalMsgID(protocol string, mID string) string {
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
br := gw.Router.getBridge(cfg.Account)
if br == nil {
gw.checkConfig(cfg)
br = bridge.New(cfg)
br.Config = gw.Router.Config
br.General = &gw.BridgeValues().General
@@ -102,6 +105,19 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
return nil
}
func (gw *Gateway) checkConfig(cfg *config.Bridge) {
match := false
for _, key := range gw.Router.Config.Viper().AllKeys() {
if strings.HasPrefix(key, cfg.Account) {
match = true
break
}
}
if !match {
gw.logger.Fatalf("Account %s defined in gateway %s but no configuration found, exiting.", cfg.Account, gw.Name)
}
}
// AddConfig associates a new configuration with the gateway object.
func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
gw.Name = cfg.Name
@@ -211,23 +227,6 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
return channels
}
// irc quit is for the whole bridge, isn't a per channel quit.
// channel is empty when we quit
if msg.Event == config.EventJoinLeave && getProtocol(msg) == "irc" && msg.Channel == "" {
// if we only have one channel on this irc bridge it's got to be the sending one.
// don't send it back
if dest.Account == msg.Account && len(dest.Channels) == 1 && dest.Protocol == "irc" {
return channels
}
for _, channel := range gw.Channels {
if channel.Account == dest.Account && strings.Contains(channel.Direction, "out") &&
gw.validGatewayDest(msg) {
channels = append(channels, *channel)
}
}
return channels
}
// if source channel is in only, do nothing
for _, channel := range gw.Channels {
// lookup the channel from the message
@@ -307,8 +306,6 @@ func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
}
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) string {
br := gw.Bridges[msg.Account]
msg.Protocol = br.Protocol
if dest.GetBool("StripNick") {
re := regexp.MustCompile("[^a-zA-Z0-9]+")
msg.Username = re.ReplaceAllString(msg.Username, "")
@@ -316,6 +313,7 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri
nick := dest.GetString("RemoteNickFormat")
// loop to replace nicks
br := gw.Bridges[msg.Account]
for _, outer := range br.GetStringSlice2D("ReplaceNicks") {
search := outer[0]
replace := outer[1]
@@ -347,6 +345,11 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri
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)
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
return nick
}
@@ -363,9 +366,12 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil {
gw.logger.Errorf("TengoModifyMessage failed: %s", err)
}
if err := modifyMessageTengo(gw.BridgeValues().Tengo.Message, msg); err != nil {
gw.logger.Errorf("Tengo.Message failed: %s", err)
}
// replace :emoji: to unicode
msg.Text = emojilib.Replace(msg.Text)
msg.Text = emoji.Sprint(msg.Text)
br := gw.Bridges[msg.Account]
// loop to replace messages
@@ -437,6 +443,11 @@ func (gw *Gateway) SendMessage(
msg.ParentID = "msg-parent-not-found"
}
err := gw.modifySendMessageTengo(rmsg, &msg, dest)
if err != nil {
gw.logger.Errorf("modifySendMessageTengo: %s", err)
}
// 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" {
@@ -502,7 +513,8 @@ func modifyMessageTengo(filename string, msg *config.Message) error {
if err != nil {
return err
}
s := script.New(res)
s := tengo.NewScript(res)
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
_ = s.Add("msgText", msg.Text)
_ = s.Add("msgUsername", msg.Username)
_ = s.Add("msgAccount", msg.Account)
@@ -518,3 +530,77 @@ func modifyMessageTengo(filename string, msg *config.Message) error {
msg.Username = c.Get("msgUsername").String()
return nil
}
func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) (string, error) {
filename := gw.BridgeValues().Tengo.RemoteNickFormat
if filename == "" {
return "", nil
}
res, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
s := tengo.NewScript(res)
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
_ = s.Add("result", "")
_ = s.Add("msgText", msg.Text)
_ = s.Add("msgUsername", msg.Username)
_ = s.Add("nick", msg.Username)
_ = s.Add("msgAccount", msg.Account)
_ = s.Add("msgChannel", msg.Channel)
_ = s.Add("channel", msg.Channel)
_ = s.Add("msgProtocol", msg.Protocol)
_ = s.Add("remoteAccount", br.Account)
_ = s.Add("protocol", br.Protocol)
_ = s.Add("bridge", br.Name)
_ = s.Add("gateway", gw.Name)
c, err := s.Compile()
if err != nil {
return "", err
}
if err := c.Run(); err != nil {
return "", err
}
return c.Get("result").String(), nil
}
func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.Message, br *bridge.Bridge) error {
filename := gw.BridgeValues().Tengo.OutMessage
var res []byte
var err error
if filename == "" {
res, err = internal.Asset("tengo/outmessage.tengo")
if err != nil {
return err
}
} else {
res, err = ioutil.ReadFile(filename)
if err != nil {
return err
}
}
s := tengo.NewScript(res)
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
_ = s.Add("inAccount", origmsg.Account)
_ = s.Add("inProtocol", origmsg.Protocol)
_ = s.Add("inChannel", origmsg.Channel)
_ = s.Add("inGateway", origmsg.Gateway)
_ = s.Add("inEvent", origmsg.Event)
_ = s.Add("outAccount", br.Account)
_ = s.Add("outProtocol", br.Protocol)
_ = s.Add("outChannel", msg.Channel)
_ = s.Add("outGateway", gw.Name)
_ = s.Add("outEvent", msg.Event)
_ = s.Add("msgText", msg.Text)
_ = s.Add("msgUsername", msg.Username)
c, err := s.Compile()
if err != nil {
return err
}
if err := c.Run(); err != nil {
return err
}
msg.Text = c.Get("msgText").String()
msg.Username = c.Get("msgUsername").String()
return nil
}

View File

@@ -15,10 +15,15 @@ import (
var testconfig = []byte(`
[irc.freenode]
server=""
[mattermost.test]
server=""
[gitter.42wim]
server=""
[discord.test]
server=""
[slack.test]
server=""
[[gateway]]
name = "bridge1"
@@ -44,10 +49,15 @@ var testconfig = []byte(`
var testconfig2 = []byte(`
[irc.freenode]
server=""
[mattermost.test]
server=""
[gitter.42wim]
server=""
[discord.test]
server=""
[slack.test]
server=""
[[gateway]]
name = "bridge1"
@@ -87,8 +97,11 @@ var testconfig2 = []byte(`
var testconfig3 = []byte(`
[irc.zzz]
server=""
[telegram.zzz]
server=""
[slack.zzz]
server=""
[[gateway]]
name="bridge"
enable=true
@@ -176,7 +189,6 @@ func TestNewRouter(t *testing.T) {
assert.Equal(t, 1, len(r.Gateways))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Channels))
r = maketestRouter(testconfig2)
assert.Equal(t, 2, len(r.Gateways))
assert.Equal(t, 4, len(r.Gateways["bridge1"].Bridges))

View File

@@ -14,6 +14,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/gateway/bridgemap"
)
// handleEventFailure handles failures and reconnects bridges.
@@ -190,6 +191,14 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
func (gw *Gateway) handleMessage(rmsg *config.Message, dest *bridge.Bridge) []*BrMsgID {
var brMsgIDs []*BrMsgID
// Not all bridges support "user is typing" indications so skip the message
// if the targeted bridge does not support it.
if rmsg.Event == config.EventUserTyping {
if _, ok := bridgemap.UserTypingSupport[dest.Protocol]; !ok {
return nil
}
}
// if we have an attached file, or other info
if rmsg.Extra != nil && len(rmsg.Extra[config.EventFileFailureSize]) != 0 && rmsg.Text == "" {
return brMsgIDs

View File

@@ -59,8 +59,14 @@ func NewRouter(rootLogger *logrus.Logger, cfg config.Config, bridgeMap map[strin
// between them.
func (r *Router) Start() error {
m := make(map[string]*bridge.Bridge)
if len(r.Gateways) == 0 {
return fmt.Errorf("no [[gateway]] configured. See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for more info")
}
for _, gw := range r.Gateways {
r.logger.Infof("Parsing gateway %s", gw.Name)
if len(gw.Bridges) == 0 {
return fmt.Errorf("no bridges configured for gateway %s. See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for more info", gw.Name)
}
for _, br := range gw.Bridges {
m[br.Account] = br
}
@@ -125,7 +131,11 @@ func (r *Router) handleReceive() {
r.handleEventGetChannelMembers(&msg)
r.handleEventFailure(&msg)
r.handleEventRejoinChannels(&msg)
idx := 0
// Set message protocol based on the account it came from
msg.Protocol = r.getBridge(msg.Account).Protocol
filesHandled := false
for _, gw := range r.Gateways {
// record all the message ID's of the different bridges
var msgIDs []*BrMsgID
@@ -134,17 +144,26 @@ func (r *Router) handleReceive() {
}
msg.Timestamp = time.Now()
gw.modifyMessage(&msg)
if idx == 0 {
if !filesHandled {
gw.handleFiles(&msg)
filesHandled = true
}
for _, br := range gw.Bridges {
msgIDs = append(msgIDs, gw.handleMessage(&msg, br)...)
}
// only add the message ID if it doesn't already exists
if _, ok := gw.Messages.Get(msg.Protocol + " " + msg.ID); !ok && msg.ID != "" {
gw.Messages.Add(msg.Protocol+" "+msg.ID, msgIDs)
if msg.ID != "" {
_, exists := gw.Messages.Get(msg.Protocol + " " + msg.ID)
// Only add the message ID if it doesn't already exist
//
// 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" {
gw.Messages.Add(msg.Protocol+" "+msg.ID, msgIDs)
}
}
idx++
}
}
}

72
go.mod
View File

@@ -3,72 +3,64 @@ 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/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
github.com/bwmarrin/discordgo v0.19.0
github.com/d5/tengo v1.12.1
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
github.com/Rhymen/go-whatsapp v0.1.0
github.com/d5/tengo/v2 v2.0.2
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/fsnotify/fsnotify v1.4.7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
github.com/google/gops v0.3.5
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497
github.com/google/gops v0.3.6
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect
github.com/gorilla/schema v1.0.2
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/golang-lru v0.5.0
github.com/gorilla/schema v1.1.0
github.com/gorilla/websocket v1.4.1
github.com/hashicorp/golang-lru v0.5.3
github.com/hpcloud/tail v1.0.0 // indirect
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7
github.com/jtolds/gls v4.2.1+incompatible // indirect
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/labstack/echo/v4 v4.0.0
github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect
github.com/jpillora/backoff v1.0.0
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e
github.com/labstack/echo/v4 v4.1.13
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e
github.com/matterbridge/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa
github.com/mattermost/mattermost-server v5.5.0+incompatible
github.com/mattn/go-runewidth v0.0.7 // indirect
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // 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/nicksnyder/go-i18n v1.4.0 // indirect
github.com/nlopes/slack v0.5.0
github.com/onsi/ginkgo v1.6.0 // indirect
github.com/onsi/gomega v1.4.1 // indirect
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320
github.com/pkg/errors v0.8.0 // indirect
github.com/rs/xid v1.2.1
github.com/russross/blackfriday v1.5.2
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
github.com/sirupsen/logrus v1.3.0
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
github.com/spf13/viper v1.3.1
github.com/stretchr/testify v1.3.0
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98
github.com/sirupsen/logrus v1.4.2
github.com/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901
github.com/spf13/viper v1.6.1
github.com/stretchr/testify v1.4.0
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 // indirect
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe // indirect
gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 // indirect
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1 // indirect
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
//replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43
//replace github.com/yaegashi/msgraph.go => github.com/matterbridge/msgraph.go v0.0.0-20191226214848-9e5d9c08a4e1
go 1.13

327
go.sum
View File

@@ -1,22 +1,40 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg=
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y=
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 h1:v/zr4ns/4sSahF9KBm4Uc933bLsEEv7LuT63CJ019yo=
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ=
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
github.com/Rhymen/go-whatsapp v0.1.0 h1:XTXhFIQ/fx9jKObUnUX2Q+nh58EyeHNhX7DniE8xeuA=
github.com/Rhymen/go-whatsapp v0.1.0/go.mod h1:xJSy+okeRjKkQEH/lEYrnekXB3PG33fqL0I6ncAkV50=
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/d5/tengo v1.12.1 h1:libKkDM95CsZgYs6E5eiEaM9sbcw2EzJRSkr9o5NO4s=
github.com/d5/tengo v1.12.1/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/d5/tengo/v2 v2.0.2 h1:3APkPZPc1FExaJoWrN5YzvDqc6GNkQH6ehmCRDmN83I=
github.com/d5/tengo/v2 v2.0.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -24,81 +42,120 @@ github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 h1:wJkj+x9gPYlDyM34C6r3SXPs270coWeh85wu1CsusDo=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to=
github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v1.3.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME=
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e h1:KbPTfR/PYuau1IzKoE4lnd8yby5I2pBj+VR6fSVbYU8=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e/go.mod h1:vNc28YFzigVJod0j5EbuTtRIe7swx8vodh2yA4jZ2s8=
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999 h1:2d+FLQbz4xRTi36DO1qYNUwfORax9XcQ0jhbO81Vago=
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.0.0 h1:q1GH+caIXPP7H2StPIdzy/ez9CO0EepqYeUg6vi9SWM=
github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398 h1:a40kRmhA1p2XFJ6gqXfCExSyuDDCp/U9LA8ZY27u2Lk=
github.com/lrstanley/girc v0.0.0-20190210212025-51b8e096d398/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk=
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU=
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0=
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 h1:iOAVXzZyXtW408TMYejlUPo6BIn92HmOacWtIfNyYns=
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/echo/v4 v4.1.13 h1:JYgKq6NQQSaKbQcsOadAKX1kUVLCUzLGwu8sxN5tC34=
github.com/labstack/echo/v4 v4.1.13/go.mod h1:3WZNypykZ3tnqpF2Qb4fPg27XDunFqgP3HGDmCMgv7U=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 h1:BS9tqL0OCiOGuy/CYYk2gc33fxqaqh5/rhqMKu4tcYA=
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b h1:cO6Z+yj4Ivq/ay/IxSrV90oSIW/SSXWLa+XHsiLKMrw=
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b/go.mod h1:dW19fYkkdUZsBAx7zv9fDh0n6NRqYIaKwB2JEBw8d0U=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4=
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k=
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc=
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0=
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e h1:1NqciL8sz+0UYeFrd/UQlL8tJPhFxOBmg+a94DN2sJU=
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e/go.mod h1:DrIFGcFumRlEW5k3PJjWGKPd4+w37d3SwOxlh1ZAL+4=
github.com/matterbridge/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa h1:ZP87nK5Mhgvt6Rpgbztdmq9+6cb3aZ6MgW/JWKbnMrI=
github.com/matterbridge/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa/go.mod h1:l0kx9L8Z+NbBCGrQ/y+ldKZ/fiwBZjPoXwDS55LTumI=
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51 h1:MpI7hy3MiCnrggmZI/s8LaPbLVOOWpzDbjA4F+XaXaM=
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51/go.mod h1:s3KUdOIXJ+jaGM++XHiXA6gikdleaWVATCcQGD4h734=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
@@ -107,48 +164,62 @@ github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteT
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9DtP8gnTmNtMxbPyhYltfxsITel8g=
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 h1:mp6tU1r0xLostUGLkTspf/9/AiHuVD7ptyXhySkDEsE=
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9/go.mod h1:A5SRAcpTemjGgIuBq6Kic2yHcoeUFWUinOAlMP/i9xo=
github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I=
github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0=
github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83 h1:XQonH5Iv5rbyIkMJOQ4xKmKHQTh8viXtRSmep5Ca5I4=
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4=
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw=
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4=
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcUhvI17yO9SlpAdDIJGkhDEgO8A=
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0=
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio=
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98 h1:sN07ff+PSRsUNhpSod4uGKAQ+Nc0FXsBPG9FmYMNg4w=
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98/go.mod h1:xkTgfD+WP+KR4HuG76oal25BBEeu5kJyi2EOsgiu/4Q=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg=
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901 h1:sXIMY2YPYEm5NoGMCrJC50N+8t9W6vbY9qr61zcLEAE=
github.com/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901/go.mod h1:ZUNi+O1Pwr2ch2UOp2AfF+s7QYQgwht2Cd1UTeIYw9A=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
@@ -157,76 +228,114 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo=
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8=
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f h1:jwXy/CsM4xS2aoiF2fHAlukmInWhd2TlWB+HDCyvzKc=
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f/go.mod h1:SIHlEr9462fpIfTrVWf3GqQDxnA65Vm3BMMsUtuA6W0=
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 h1:wD/sPUgx2QJFPTyXZpJnLaROolfeKuruh06U4pRV0WY=
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2/go.mod h1:wQk4rLkWrdOPjUAtqJRJ10hIlseLSVYWP95PLrjDF9s=
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe h1:5kUPFAF52umOUPH12MuNUmyVTseJRNBftDl/KfsvX3I=
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe/go.mod h1:P9LSM1KVzrIstFgUaveuwiAm8PK5VTB3yJEU8kqlbrU=
gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJWpU5qHKGNy///1pacZC9txiUI=
gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY=
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 h1:+vH8qNweCrORN49012OX3h0oWEXO3p+rRnpAGQinddk=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20171017063910-8dbc5d05d6ed/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o=
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI=
maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI=
maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=

288
internal/bindata.go Normal file
View File

@@ -0,0 +1,288 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
// tengo/outmessage.tengo
package internal
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info fileInfoEx
}
type fileInfoEx interface {
os.FileInfo
MD5Checksum() string
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
md5checksum string
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) MD5Checksum() string {
return fi.md5checksum
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _bindataTengoOutmessagetengo = []byte(
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x91\x3d\x8f\xda\x40\x10\x86\xfb\xfd\x15\x13\x37\xb1\x2d\x07\xe7\xa3" +
"\xb3\x64\x59\x11\x45\x94\x2e\x8a\x92\x0a\xd0\xb1\xac\x07\x33\xd2\x7a\xc7\x1a\x8f\x31\x88\xe3\xbf\x9f\xcc\x01\x47" +
"\x7f\xc5\x75\xef\xae\x9e\x9d\x77\x1f\x4d\x9e\x9a\xbd\x15\xb2\x1b\x8f\x3d\xd8\xbd\x25\x3f\x45\x30\x82\xb6\xfe\xc2" +
"\xc1\x1f\x0b\x43\xe1\xa7\x73\x3c\x04\xcd\x80\xc2\x1f\x61\x65\xc7\x7e\xca\xf3\x9d\x0d\x01\x2f\xf1\x97\x55\x1c\xed" +
"\xd1\xf0\xa0\x77\x98\x07\x7d\xa3\x79\xd0\x3b\xce\x83\xde\xf8\xd7\x9e\x51\x48\xb1\x30\x6d\xdf\xfc\xc3\x83\x66\xd0" +
"\xf6\xcd\xff\x1e\x25\xd8\x16\x4d\x9a\x1b\xa3\x78\x50\x28\x4a\xa0\xb6\x63\xd1\x38\x9a\xce\x51\x62\x4c\x9e\x43\xaf" +
"\x42\x1d\x90\x38\x70\xec\x59\xfa\xe9\x8e\xb6\x30\xe2\x67\x41\x08\xac\xd0\x63\xa8\x29\x34\xa0\x0c\x36\x5c\xc0\x8d" +
"\x50\xdd\x20\x8c\x78\x7d\xac\x3b\x84\xdf\x7f\xe7\xb7\x01\xb4\x7d\xd0\x84\xb2\x84\x88\xc4\x45\x70\x32\x00\x00\x82" +
"\xd3\x3f\xa6\xfe\x99\xe0\x93\xe3\xb6\x23\x8f\xf1\x7a\x79\xf8\xfa\x23\xae\x8a\x65\x7d\xfa\x96\x7d\x3f\xc7\x55\x91" +
"\x5d\x63\x52\x25\xd5\xf3\x62\x51\xb8\xa0\xe2\x8b\xd5\x6a\x9d\x5c\xc6\x5c\x4d\x4b\xc1\x99\x60\xe7\xad\xc3\xf8\x26" +
"\x1f\x45\x89\x39\x9b\xf7\x6b\xe4\x29\x6d\x1f\x57\x00\x9f\x3e\xc6\x24\xcd\xcd\x4b\x00\x00\x00\xff\xff\x40\xb8\x54" +
"\xb8\x64\x02\x00\x00")
func bindataTengoOutmessagetengoBytes() ([]byte, error) {
return bindataRead(
_bindataTengoOutmessagetengo,
"tengo/outmessage.tengo",
)
}
func bindataTengoOutmessagetengo() (*asset, error) {
bytes, err := bindataTengoOutmessagetengoBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{
name: "tengo/outmessage.tengo",
size: 612,
md5checksum: "",
mode: os.FileMode(420),
modTime: time.Unix(1555622139, 0),
}
a := &asset{bytes: bytes, info: info}
return a, nil
}
//
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
//
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
}
//
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
// nolint: deadcode
//
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
//
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or could not be loaded.
//
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
}
//
// AssetNames returns the names of the assets.
// nolint: deadcode
//
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
//
// _bindata is a table, holding each asset generator, mapped to its name.
//
var _bindata = map[string]func() (*asset, error){
"tengo/outmessage.tengo": bindataTengoOutmessagetengo,
}
//
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
//
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
}
}
if node.Func != nil {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{Func: nil, Children: map[string]*bintree{
"tengo": {Func: nil, Children: map[string]*bintree{
"outmessage.tengo": {Func: bindataTengoOutmessagetengo, Children: map[string]*bintree{}},
}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@@ -0,0 +1,25 @@
/*
variables available
read-only:
inAccount, inProtocol, inChannel, inGateway
outAccount, outProtocol, outChannel, outGateway
read-write:
msgText, msgUsername
*/
text := import("text")
// start - strip irc colors
// if we're not sending to an irc bridge we strip the IRC colors
if inProtocol == "irc" && outProtocol != "irc" {
re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
msgText=re.replace(msgText,"")
}
// end - strip irc colors
// strip custom emoji
if inProtocol == "discord" {
re := text.re_compile(`<a?(:.*?:)[0-9]+>`)
msgText=re.replace(msgText,"$1")
}

View File

@@ -15,7 +15,7 @@ import (
)
var (
version = "1.14.0"
version = "1.17.0"
githash string
flagConfig = flag.String("conf", "matterbridge.toml", "config file")

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,16 @@ func (m *MMClient) GetChannelHeader(channelId string) string { //nolint:golint
return ""
}
func getNormalisedName(channel *model.Channel) string {
if channel.Type == model.CHANNEL_GROUP {
// (deprecated in favor of ReplaceAll in go 1.12)
res := strings.Replace(channel.DisplayName, ", ", "-", -1) //nolint: gocritic
res = strings.Replace(res, " ", "_", -1) //nolint: gocritic
return res
}
return channel.Name
}
func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:golint
m.RLock()
defer m.RUnlock()
@@ -45,14 +55,9 @@ func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:go
for _, t := range m.OtherTeams {
for _, channel := range append(t.Channels, t.MoreChannels...) {
if channel.Type == model.CHANNEL_GROUP {
res := strings.Replace(channel.DisplayName, ", ", "-", -1)
res = strings.Replace(res, " ", "_", -1)
if res == name {
return channel.Id
}
if getNormalisedName(channel) == name {
return channel.Id
}
}
}
return ""
@@ -62,7 +67,7 @@ func (m *MMClient) getChannelIdTeam(name string, teamId string) string { //nolin
for _, t := range m.OtherTeams {
if t.Id == teamId {
for _, channel := range append(t.Channels, t.MoreChannels...) {
if channel.Name == name {
if getNormalisedName(channel) == name {
return channel.Id
}
}
@@ -80,12 +85,7 @@ func (m *MMClient) GetChannelName(channelId string) string { //nolint:golint
}
for _, channel := range append(t.Channels, t.MoreChannels...) {
if channel.Id == channelId {
if channel.Type == model.CHANNEL_GROUP {
res := strings.Replace(channel.DisplayName, ", ", "-", -1)
res = strings.Replace(res, " ", "_", -1)
return res
}
return channel.Name
return getNormalisedName(channel)
}
}
}
@@ -166,23 +166,42 @@ func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint
return nil
}
func (m *MMClient) UpdateChannelsTeam(teamID string) error {
mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
for idx, t := range m.OtherTeams {
if t.Id == teamID {
m.Lock()
m.OtherTeams[idx].Channels = mmchannels
m.Unlock()
}
}
mmchannels, resp = m.Client.GetPublicChannelsForTeam(teamID, 0, 5000, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
for idx, t := range m.OtherTeams {
if t.Id == teamID {
m.Lock()
m.OtherTeams[idx].MoreChannels = mmchannels
m.Unlock()
}
}
return nil
}
func (m *MMClient) UpdateChannels() error {
mmchannels, resp := m.Client.GetChannelsForTeamForUser(m.Team.Id, m.User.Id, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
if err := m.UpdateChannelsTeam(m.Team.Id); err != nil {
return err
}
m.Lock()
m.Team.Channels = mmchannels
m.Unlock()
mmchannels, resp = m.Client.GetPublicChannelsForTeam(m.Team.Id, 0, 5000, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
for _, t := range m.OtherTeams {
if err := m.UpdateChannelsTeam(t.Id); err != nil {
return err
}
}
m.Lock()
m.Team.MoreChannels = mmchannels
m.Unlock()
return nil
}

View File

@@ -132,14 +132,25 @@ func (m *MMClient) initUser() error {
return resp.Error
}
for _, team := range teams {
mmusers, resp := m.Client.GetUsersInTeam(team.Id, 0, 50000, "")
idx := 0
max := 200
usermap := make(map[string]*model.User)
mmusers, resp := m.Client.GetUsersInTeam(team.Id, idx, max, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
usermap := make(map[string]*model.User)
for _, user := range mmusers {
usermap[user.Id] = user
for len(mmusers) > 0 {
for _, user := range mmusers {
usermap[user.Id] = user
}
mmusers, resp = m.Client.GetUsersInTeam(team.Id, idx, max, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
idx++
time.Sleep(time.Millisecond * 200)
}
m.logger.Infof("found %d users in team %s", len(usermap), team.Name)
t := &Team{Team: team, Users: usermap, Id: team.Id}
@@ -175,15 +186,19 @@ func (m *MMClient) serverAlive(firstConnection bool, b *backoff.Backoff) error {
if resp.Error != nil {
return fmt.Errorf("%#v", resp.Error.Error())
}
if firstConnection && !supportedVersion(resp.ServerVersion) {
if firstConnection && !m.SkipVersionCheck && !supportedVersion(resp.ServerVersion) {
return fmt.Errorf("unsupported mattermost version: %s", resp.ServerVersion)
}
m.ServerVersion = resp.ServerVersion
if m.ServerVersion == "" {
m.logger.Debugf("Server not up yet, reconnecting in %s", d)
time.Sleep(d)
if !m.SkipVersionCheck {
m.ServerVersion = resp.ServerVersion
if m.ServerVersion == "" {
m.logger.Debugf("Server not up yet, reconnecting in %s", d)
time.Sleep(d)
} else {
m.logger.Infof("Found version %s", m.ServerVersion)
return nil
}
} else {
m.logger.Infof("Found version %s", m.ServerVersion)
return nil
}
}

View File

@@ -16,14 +16,15 @@ import (
)
type Credentials struct {
Login string
Team string
Pass string
Token string
CookieToken bool
Server string
NoTLS bool
SkipTLSVerify bool
Login string
Team string
Pass string
Token string
CookieToken bool
Server string
NoTLS bool
SkipTLSVerify bool
SkipVersionCheck bool
}
type Message struct {
@@ -216,9 +217,17 @@ func (m *MMClient) WsReceiver() {
if msg.Post != nil {
if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" {
m.MessageChan <- msg
continue
}
}
continue
switch msg.Raw.Event {
case model.WEBSOCKET_EVENT_USER_ADDED,
model.WEBSOCKET_EVENT_USER_REMOVED,
model.WEBSOCKET_EVENT_CHANNEL_CREATED,
model.WEBSOCKET_EVENT_CHANNEL_DELETED:
m.MessageChan <- msg
continue
}
}
var response model.WebSocketResponse

View File

@@ -83,7 +83,7 @@ func (m *MMClient) DeleteMessage(postId string) error { //nolint:golint
}
func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint
post := &model.Post{Message: text}
post := &model.Post{Message: text, Id: postId}
res, resp := m.Client.UpdatePost(postId, post)
if resp.Error != nil {
return "", resp.Error

View File

@@ -2,6 +2,7 @@ package matterclient
import (
"errors"
"time"
"github.com/mattermost/mattermost-server/model"
)
@@ -99,15 +100,25 @@ func (m *MMClient) GetUsers() map[string]*model.User {
}
func (m *MMClient) UpdateUsers() error {
mmusers, resp := m.Client.GetUsers(0, 50000, "")
idx := 0
max := 200
mmusers, resp := m.Client.GetUsers(idx, max, "")
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
m.Lock()
for _, user := range mmusers {
m.Users[user.Id] = user
for len(mmusers) > 0 {
m.Lock()
for _, user := range mmusers {
m.Users[user.Id] = user
}
m.Unlock()
mmusers, resp = m.Client.GetUsers(idx, max, "")
time.Sleep(time.Millisecond * 300)
if resp.Error != nil {
return errors.New(resp.Error.DetailedError)
}
idx++
}
m.Unlock()
return nil
}

View File

@@ -14,7 +14,7 @@ import (
"time"
"github.com/gorilla/schema"
"github.com/nlopes/slack"
"github.com/slack-go/slack"
)
// OMessage for mattermost incoming webhook. (send to mattermost)

View File

@@ -45,20 +45,11 @@ Whether you want to develop your own Steam bot or directly work on go-steam itse
## Updating go-steam to a new SteamKit version
To update go-steam to a new version of SteamKit, do the following:
Go source code is generated with code in the `generator` directory.
Look at `generator/README.md` for more information on how to use the generator.
go get github.com/golang/protobuf/protoc-gen-go/
git submodule init && git submodule update
cd generator
go run generator.go clean proto steamlang
Make sure that `$GOPATH/bin` / `protoc-gen-go` is in your `$PATH`. You'll also need [`protoc`](https://developers.google.com/protocol-buffers/docs/downloads), the protocol buffer compiler. At the moment, we use Protocol Buffers 2.6.1 with `proco-gen-go`-[2402d76](https://github.com/golang/protobuf/tree/2402d76f3d41f928c7902a765dfc872356dd3aad).
To compile the Steam Language files, you also need the [.NET Framework](https://www.microsoft.com/net/downloads)
on Windows or [mono](http://www.go-mono.com/mono-downloads/download.html) on other operating systems.
Apply the protocol changes where necessary.
Then, after generating new Go source files, update `go-steam` as necessary.
## License
Steam for Go is licensed under the New BSD License. More information can be found in LICENSE.txt.
Steam for Go is licensed under the New BSD License. More information can be found in LICENSE.txt.

View File

@@ -94,9 +94,6 @@ func (a *Auth) HandlePacket(packet *Packet) {
a.handleUpdateMachineAuth(packet)
case EMsg_ClientAccountInfo:
a.handleAccountInfo(packet)
case EMsg_ClientWalletInfoUpdate:
case EMsg_ClientRequestWebAPIAuthenticateUserNonceResponse:
case EMsg_ClientMarketingMessageUpdate:
}
}

View File

@@ -133,11 +133,17 @@ func (c *Client) Connected() bool {
// If you want to connect to a specific server, use `ConnectTo`.
func (c *Client) Connect() *netutil.PortAddr {
var server *netutil.PortAddr
// try to initialize the directory cache
if !steamDirectoryCache.IsInitialized() {
_ = steamDirectoryCache.Initialize()
}
if steamDirectoryCache.IsInitialized() {
server = steamDirectoryCache.GetRandomCM()
} else {
server = GetRandomCM()
}
c.ConnectTo(server)
return server
}

View File

@@ -1,6 +1,7 @@
package protocol
import (
"encoding/hex"
"io"
"math"
"strconv"
@@ -42,6 +43,7 @@ const EClientPersonaStateFlag_DefaultInfoRequest = EClientPersonaStateFlag_Playe
const DefaultAvatar = "fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb"
func ValidAvatar(avatar string) bool {
return !(avatar == "0000000000000000000000000000000000000000" || len(avatar) != 40)
func ValidAvatar(avatar []byte) bool {
str := hex.EncodeToString(avatar)
return !(str == "0000000000000000000000000000000000000000" || len(str) != 40)
}

View File

@@ -1,31 +1,60 @@
// Code generated by protoc-gen-go.
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: encrypted_app_ticket.proto
// DO NOT EDIT!
package protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package protobuf is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package protobuf to be updated.
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"`
XXX_unrecognized []byte `json:"-"`
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EncryptedAppTicket) Reset() { *m = EncryptedAppTicket{} }
func (m *EncryptedAppTicket) String() string { return proto.CompactTextString(m) }
func (*EncryptedAppTicket) ProtoMessage() {}
func (*EncryptedAppTicket) Descriptor() ([]byte, []int) { return app_ticket_fileDescriptor0, []int{0} }
func (m *EncryptedAppTicket) Reset() { *m = EncryptedAppTicket{} }
func (m *EncryptedAppTicket) String() string { return proto.CompactTextString(m) }
func (*EncryptedAppTicket) ProtoMessage() {}
func (*EncryptedAppTicket) Descriptor() ([]byte, []int) {
return fileDescriptor_c6d69fd1cac4e8d5, []int{0}
}
func (m *EncryptedAppTicket) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EncryptedAppTicket.Unmarshal(m, b)
}
func (m *EncryptedAppTicket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EncryptedAppTicket.Marshal(b, m, deterministic)
}
func (m *EncryptedAppTicket) XXX_Merge(src proto.Message) {
xxx_messageInfo_EncryptedAppTicket.Merge(m, src)
}
func (m *EncryptedAppTicket) XXX_Size() int {
return xxx_messageInfo_EncryptedAppTicket.Size(m)
}
func (m *EncryptedAppTicket) XXX_DiscardUnknown() {
xxx_messageInfo_EncryptedAppTicket.DiscardUnknown(m)
}
var xxx_messageInfo_EncryptedAppTicket proto.InternalMessageInfo
func (m *EncryptedAppTicket) GetTicketVersionNo() uint32 {
if m != nil && m.TicketVersionNo != nil {
@@ -66,17 +95,19 @@ func init() {
proto.RegisterType((*EncryptedAppTicket)(nil), "EncryptedAppTicket")
}
var app_ticket_fileDescriptor0 = []byte{
// 162 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xcd, 0x4b, 0x2e,
func init() { proto.RegisterFile("encrypted_app_ticket.proto", fileDescriptor_c6d69fd1cac4e8d5) }
var fileDescriptor_c6d69fd1cac4e8d5 = []byte{
// 164 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, 0x01, 0x2d, 0xc8, 0xc8, 0x2c, 0x80, 0x1a, 0xc3,
0x02, 0x56, 0x28, 0xc1, 0x25, 0x80, 0x50, 0x05, 0x95, 0x61, 0x05, 0xca, 0xf0, 0x38, 0xb1, 0x7a,
0x30, 0x36, 0x30, 0x32, 0x00, 0x02, 0x00, 0x00, 0xff, 0xff, 0x03, 0x8c, 0xdb, 0x92, 0xd3, 0x00,
0x00, 0x00,
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,
}

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,397 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: steammessages_sitelicenseclient.proto
package protobuf
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package protobuf is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package protobuf to be updated.
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteInfo) Reset() { *m = CMsgClientSiteInfo{} }
func (m *CMsgClientSiteInfo) String() string { return proto.CompactTextString(m) }
func (*CMsgClientSiteInfo) ProtoMessage() {}
func (*CMsgClientSiteInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{0}
}
func (m *CMsgClientSiteInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteInfo.Unmarshal(m, b)
}
func (m *CMsgClientSiteInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteInfo.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteInfo.Merge(m, src)
}
func (m *CMsgClientSiteInfo) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteInfo.Size(m)
}
func (m *CMsgClientSiteInfo) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteInfo.DiscardUnknown(m)
}
var xxx_messageInfo_CMsgClientSiteInfo proto.InternalMessageInfo
func (m *CMsgClientSiteInfo) GetSiteId() uint64 {
if m != nil && m.SiteId != nil {
return *m.SiteId
}
return 0
}
func (m *CMsgClientSiteInfo) GetSiteName() string {
if m != nil && m.SiteName != nil {
return *m.SiteName
}
return ""
}
func (m *CMsgClientSiteInfo) GetAllowCachedCredentials() bool {
if m != nil && m.AllowCachedCredentials != nil {
return *m.AllowCachedCredentials
}
return false
}
type CMsgClientSiteLicenseCheckout struct {
Appid *uint32 `protobuf:"varint,1,opt,name=appid" json:"appid,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteLicenseCheckout) Reset() { *m = CMsgClientSiteLicenseCheckout{} }
func (m *CMsgClientSiteLicenseCheckout) String() string { return proto.CompactTextString(m) }
func (*CMsgClientSiteLicenseCheckout) ProtoMessage() {}
func (*CMsgClientSiteLicenseCheckout) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{1}
}
func (m *CMsgClientSiteLicenseCheckout) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteLicenseCheckout.Unmarshal(m, b)
}
func (m *CMsgClientSiteLicenseCheckout) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteLicenseCheckout.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteLicenseCheckout) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteLicenseCheckout.Merge(m, src)
}
func (m *CMsgClientSiteLicenseCheckout) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteLicenseCheckout.Size(m)
}
func (m *CMsgClientSiteLicenseCheckout) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteLicenseCheckout.DiscardUnknown(m)
}
var xxx_messageInfo_CMsgClientSiteLicenseCheckout proto.InternalMessageInfo
func (m *CMsgClientSiteLicenseCheckout) GetAppid() uint32 {
if m != nil && m.Appid != nil {
return *m.Appid
}
return 0
}
type CMsgClientSiteLicenseCheckoutResponse struct {
Eresult *int32 `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteLicenseCheckoutResponse) Reset() { *m = CMsgClientSiteLicenseCheckoutResponse{} }
func (m *CMsgClientSiteLicenseCheckoutResponse) String() string { return proto.CompactTextString(m) }
func (*CMsgClientSiteLicenseCheckoutResponse) ProtoMessage() {}
func (*CMsgClientSiteLicenseCheckoutResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{2}
}
func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Unmarshal(m, b)
}
func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Merge(m, src)
}
func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.Size(m)
}
func (m *CMsgClientSiteLicenseCheckoutResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CMsgClientSiteLicenseCheckoutResponse proto.InternalMessageInfo
const Default_CMsgClientSiteLicenseCheckoutResponse_Eresult int32 = 2
func (m *CMsgClientSiteLicenseCheckoutResponse) GetEresult() int32 {
if m != nil && m.Eresult != nil {
return *m.Eresult
}
return Default_CMsgClientSiteLicenseCheckoutResponse_Eresult
}
type CMsgClientSiteLicenseGetAvailableSeats struct {
Appid *uint32 `protobuf:"varint,1,opt,name=appid" json:"appid,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) Reset() {
*m = CMsgClientSiteLicenseGetAvailableSeats{}
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) String() string { return proto.CompactTextString(m) }
func (*CMsgClientSiteLicenseGetAvailableSeats) ProtoMessage() {}
func (*CMsgClientSiteLicenseGetAvailableSeats) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{3}
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Unmarshal(m, b)
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Merge(m, src)
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.Size(m)
}
func (m *CMsgClientSiteLicenseGetAvailableSeats) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats.DiscardUnknown(m)
}
var xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeats proto.InternalMessageInfo
func (m *CMsgClientSiteLicenseGetAvailableSeats) GetAppid() uint32 {
if m != nil && m.Appid != nil {
return *m.Appid
}
return 0
}
type CMsgClientSiteLicenseGetAvailableSeatsResponse struct {
Eresult *int32 `protobuf:"varint,1,opt,name=eresult,def=2" json:"eresult,omitempty"`
Seats *uint32 `protobuf:"varint,2,opt,name=seats" json:"seats,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) Reset() {
*m = CMsgClientSiteLicenseGetAvailableSeatsResponse{}
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) String() string {
return proto.CompactTextString(m)
}
func (*CMsgClientSiteLicenseGetAvailableSeatsResponse) ProtoMessage() {}
func (*CMsgClientSiteLicenseGetAvailableSeatsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{4}
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Unmarshal(m, b)
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Merge(m, src)
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.Size(m)
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CMsgClientSiteLicenseGetAvailableSeatsResponse proto.InternalMessageInfo
const Default_CMsgClientSiteLicenseGetAvailableSeatsResponse_Eresult int32 = 2
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) GetEresult() int32 {
if m != nil && m.Eresult != nil {
return *m.Eresult
}
return Default_CMsgClientSiteLicenseGetAvailableSeatsResponse_Eresult
}
func (m *CMsgClientSiteLicenseGetAvailableSeatsResponse) GetSeats() uint32 {
if m != nil && m.Seats != nil {
return *m.Seats
}
return 0
}
type CMsgClientSiteLicenseGetContentCacheInfo struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) Reset() {
*m = CMsgClientSiteLicenseGetContentCacheInfo{}
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) String() string { return proto.CompactTextString(m) }
func (*CMsgClientSiteLicenseGetContentCacheInfo) ProtoMessage() {}
func (*CMsgClientSiteLicenseGetContentCacheInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{5}
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Unmarshal(m, b)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Merge(m, src)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.Size(m)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfo) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfo.DiscardUnknown(m)
}
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) Reset() {
*m = CMsgClientSiteLicenseGetContentCacheInfoResponse{}
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) String() string {
return proto.CompactTextString(m)
}
func (*CMsgClientSiteLicenseGetContentCacheInfoResponse) ProtoMessage() {}
func (*CMsgClientSiteLicenseGetContentCacheInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0a32817a56a37a6e, []int{6}
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Unmarshal(m, b)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Marshal(b, m, deterministic)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Merge(m, src)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_Size() int {
return xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.Size(m)
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CMsgClientSiteLicenseGetContentCacheInfoResponse proto.InternalMessageInfo
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetUseCache() bool {
if m != nil && m.UseCache != nil {
return *m.UseCache
}
return false
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetIpv4Address() uint32 {
if m != nil && m.Ipv4Address != nil {
return *m.Ipv4Address
}
return 0
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetPortNumber() uint32 {
if m != nil && m.PortNumber != nil {
return *m.PortNumber
}
return 0
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetP2PGroup() uint32 {
if m != nil && m.P2PGroup != nil {
return *m.P2PGroup
}
return 0
}
func (m *CMsgClientSiteLicenseGetContentCacheInfoResponse) GetIpAddress() string {
if m != nil && m.IpAddress != nil {
return *m.IpAddress
}
return ""
}
func init() {
proto.RegisterType((*CMsgClientSiteInfo)(nil), "CMsgClientSiteInfo")
proto.RegisterType((*CMsgClientSiteLicenseCheckout)(nil), "CMsgClientSiteLicenseCheckout")
proto.RegisterType((*CMsgClientSiteLicenseCheckoutResponse)(nil), "CMsgClientSiteLicenseCheckoutResponse")
proto.RegisterType((*CMsgClientSiteLicenseGetAvailableSeats)(nil), "CMsgClientSiteLicenseGetAvailableSeats")
proto.RegisterType((*CMsgClientSiteLicenseGetAvailableSeatsResponse)(nil), "CMsgClientSiteLicenseGetAvailableSeatsResponse")
proto.RegisterType((*CMsgClientSiteLicenseGetContentCacheInfo)(nil), "CMsgClientSiteLicenseGetContentCacheInfo")
proto.RegisterType((*CMsgClientSiteLicenseGetContentCacheInfoResponse)(nil), "CMsgClientSiteLicenseGetContentCacheInfoResponse")
}
func init() {
proto.RegisterFile("steammessages_sitelicenseclient.proto", fileDescriptor_0a32817a56a37a6e)
}
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,
}

View File

@@ -1,27 +1,56 @@
// Code generated by protoc-gen-go.
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: content_manifest.proto
// DO NOT EDIT!
package protobuf
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package protobuf is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package protobuf to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ContentManifestPayload struct {
Mappings []*ContentManifestPayload_FileMapping `protobuf:"bytes,1,rep,name=mappings" json:"mappings,omitempty"`
XXX_unrecognized []byte `json:"-"`
Mappings []*ContentManifestPayload_FileMapping `protobuf:"bytes,1,rep,name=mappings" json:"mappings,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentManifestPayload) Reset() { *m = ContentManifestPayload{} }
func (m *ContentManifestPayload) String() string { return proto.CompactTextString(m) }
func (*ContentManifestPayload) ProtoMessage() {}
func (*ContentManifestPayload) Descriptor() ([]byte, []int) { return content_manifest_fileDescriptor0, []int{0} }
func (m *ContentManifestPayload) Reset() { *m = ContentManifestPayload{} }
func (m *ContentManifestPayload) String() string { return proto.CompactTextString(m) }
func (*ContentManifestPayload) ProtoMessage() {}
func (*ContentManifestPayload) Descriptor() ([]byte, []int) {
return fileDescriptor_e3cda137a29253ba, []int{0}
}
func (m *ContentManifestPayload) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentManifestPayload.Unmarshal(m, b)
}
func (m *ContentManifestPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentManifestPayload.Marshal(b, m, deterministic)
}
func (m *ContentManifestPayload) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentManifestPayload.Merge(m, src)
}
func (m *ContentManifestPayload) XXX_Size() int {
return xxx_messageInfo_ContentManifestPayload.Size(m)
}
func (m *ContentManifestPayload) XXX_DiscardUnknown() {
xxx_messageInfo_ContentManifestPayload.DiscardUnknown(m)
}
var xxx_messageInfo_ContentManifestPayload proto.InternalMessageInfo
func (m *ContentManifestPayload) GetMappings() []*ContentManifestPayload_FileMapping {
if m != nil {
@@ -31,23 +60,43 @@ func (m *ContentManifestPayload) GetMappings() []*ContentManifestPayload_FileMap
}
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"`
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_unrecognized []byte `json:"-"`
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"`
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:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentManifestPayload_FileMapping) Reset() { *m = ContentManifestPayload_FileMapping{} }
func (m *ContentManifestPayload_FileMapping) String() string { return proto.CompactTextString(m) }
func (*ContentManifestPayload_FileMapping) ProtoMessage() {}
func (*ContentManifestPayload_FileMapping) Descriptor() ([]byte, []int) {
return content_manifest_fileDescriptor0, []int{0, 0}
return fileDescriptor_e3cda137a29253ba, []int{0, 0}
}
func (m *ContentManifestPayload_FileMapping) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentManifestPayload_FileMapping.Unmarshal(m, b)
}
func (m *ContentManifestPayload_FileMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentManifestPayload_FileMapping.Marshal(b, m, deterministic)
}
func (m *ContentManifestPayload_FileMapping) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentManifestPayload_FileMapping.Merge(m, src)
}
func (m *ContentManifestPayload_FileMapping) XXX_Size() int {
return xxx_messageInfo_ContentManifestPayload_FileMapping.Size(m)
}
func (m *ContentManifestPayload_FileMapping) XXX_DiscardUnknown() {
xxx_messageInfo_ContentManifestPayload_FileMapping.DiscardUnknown(m)
}
var xxx_messageInfo_ContentManifestPayload_FileMapping proto.InternalMessageInfo
func (m *ContentManifestPayload_FileMapping) GetFilename() string {
if m != nil && m.Filename != nil {
return *m.Filename
@@ -98,12 +147,14 @@ func (m *ContentManifestPayload_FileMapping) GetLinktarget() string {
}
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"`
XXX_unrecognized []byte `json:"-"`
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentManifestPayload_FileMapping_ChunkData) Reset() {
@@ -114,9 +165,27 @@ func (m *ContentManifestPayload_FileMapping_ChunkData) String() string {
}
func (*ContentManifestPayload_FileMapping_ChunkData) ProtoMessage() {}
func (*ContentManifestPayload_FileMapping_ChunkData) Descriptor() ([]byte, []int) {
return content_manifest_fileDescriptor0, []int{0, 0, 0}
return fileDescriptor_e3cda137a29253ba, []int{0, 0, 0}
}
func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Unmarshal(m, b)
}
func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Marshal(b, m, deterministic)
}
func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Merge(m, src)
}
func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_Size() int {
return xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.Size(m)
}
func (m *ContentManifestPayload_FileMapping_ChunkData) XXX_DiscardUnknown() {
xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData.DiscardUnknown(m)
}
var xxx_messageInfo_ContentManifestPayload_FileMapping_ChunkData proto.InternalMessageInfo
func (m *ContentManifestPayload_FileMapping_ChunkData) GetSha() []byte {
if m != nil {
return m.Sha
@@ -153,22 +222,44 @@ 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"`
XXX_unrecognized []byte `json:"-"`
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentManifestMetadata) Reset() { *m = ContentManifestMetadata{} }
func (m *ContentManifestMetadata) String() string { return proto.CompactTextString(m) }
func (*ContentManifestMetadata) ProtoMessage() {}
func (*ContentManifestMetadata) Descriptor() ([]byte, []int) { return content_manifest_fileDescriptor0, []int{1} }
func (m *ContentManifestMetadata) Reset() { *m = ContentManifestMetadata{} }
func (m *ContentManifestMetadata) String() string { return proto.CompactTextString(m) }
func (*ContentManifestMetadata) ProtoMessage() {}
func (*ContentManifestMetadata) Descriptor() ([]byte, []int) {
return fileDescriptor_e3cda137a29253ba, []int{1}
}
func (m *ContentManifestMetadata) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentManifestMetadata.Unmarshal(m, b)
}
func (m *ContentManifestMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentManifestMetadata.Marshal(b, m, deterministic)
}
func (m *ContentManifestMetadata) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentManifestMetadata.Merge(m, src)
}
func (m *ContentManifestMetadata) XXX_Size() int {
return xxx_messageInfo_ContentManifestMetadata.Size(m)
}
func (m *ContentManifestMetadata) XXX_DiscardUnknown() {
xxx_messageInfo_ContentManifestMetadata.DiscardUnknown(m)
}
var xxx_messageInfo_ContentManifestMetadata proto.InternalMessageInfo
func (m *ContentManifestMetadata) GetDepotId() uint32 {
if m != nil && m.DepotId != nil {
@@ -234,14 +325,36 @@ func (m *ContentManifestMetadata) GetCrcClear() uint32 {
}
type ContentManifestSignature struct {
Signature []byte `protobuf:"bytes,1,opt,name=signature" json:"signature,omitempty"`
XXX_unrecognized []byte `json:"-"`
Signature []byte `protobuf:"bytes,1,opt,name=signature" json:"signature,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentManifestSignature) Reset() { *m = ContentManifestSignature{} }
func (m *ContentManifestSignature) String() string { return proto.CompactTextString(m) }
func (*ContentManifestSignature) ProtoMessage() {}
func (*ContentManifestSignature) Descriptor() ([]byte, []int) { return content_manifest_fileDescriptor0, []int{2} }
func (m *ContentManifestSignature) Reset() { *m = ContentManifestSignature{} }
func (m *ContentManifestSignature) String() string { return proto.CompactTextString(m) }
func (*ContentManifestSignature) ProtoMessage() {}
func (*ContentManifestSignature) Descriptor() ([]byte, []int) {
return fileDescriptor_e3cda137a29253ba, []int{2}
}
func (m *ContentManifestSignature) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentManifestSignature.Unmarshal(m, b)
}
func (m *ContentManifestSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentManifestSignature.Marshal(b, m, deterministic)
}
func (m *ContentManifestSignature) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentManifestSignature.Merge(m, src)
}
func (m *ContentManifestSignature) XXX_Size() int {
return xxx_messageInfo_ContentManifestSignature.Size(m)
}
func (m *ContentManifestSignature) XXX_DiscardUnknown() {
xxx_messageInfo_ContentManifestSignature.DiscardUnknown(m)
}
var xxx_messageInfo_ContentManifestSignature proto.InternalMessageInfo
func (m *ContentManifestSignature) GetSignature() []byte {
if m != nil {
@@ -250,40 +363,184 @@ func (m *ContentManifestSignature) GetSignature() []byte {
return nil
}
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"`
DeltaChunks []*ContentDeltaChunks_DeltaChunk `protobuf:"bytes,4,rep,name=deltaChunks" json:"deltaChunks,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentDeltaChunks) Reset() { *m = ContentDeltaChunks{} }
func (m *ContentDeltaChunks) String() string { return proto.CompactTextString(m) }
func (*ContentDeltaChunks) ProtoMessage() {}
func (*ContentDeltaChunks) Descriptor() ([]byte, []int) {
return fileDescriptor_e3cda137a29253ba, []int{3}
}
func (m *ContentDeltaChunks) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentDeltaChunks.Unmarshal(m, b)
}
func (m *ContentDeltaChunks) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentDeltaChunks.Marshal(b, m, deterministic)
}
func (m *ContentDeltaChunks) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentDeltaChunks.Merge(m, src)
}
func (m *ContentDeltaChunks) XXX_Size() int {
return xxx_messageInfo_ContentDeltaChunks.Size(m)
}
func (m *ContentDeltaChunks) XXX_DiscardUnknown() {
xxx_messageInfo_ContentDeltaChunks.DiscardUnknown(m)
}
var xxx_messageInfo_ContentDeltaChunks proto.InternalMessageInfo
func (m *ContentDeltaChunks) GetDepotId() uint32 {
if m != nil && m.DepotId != nil {
return *m.DepotId
}
return 0
}
func (m *ContentDeltaChunks) GetManifestIdSource() uint64 {
if m != nil && m.ManifestIdSource != nil {
return *m.ManifestIdSource
}
return 0
}
func (m *ContentDeltaChunks) GetManifestIdTarget() uint64 {
if m != nil && m.ManifestIdTarget != nil {
return *m.ManifestIdTarget
}
return 0
}
func (m *ContentDeltaChunks) GetDeltaChunks() []*ContentDeltaChunks_DeltaChunk {
if m != nil {
return m.DeltaChunks
}
return nil
}
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"`
Chunk []byte `protobuf:"bytes,5,opt,name=chunk" json:"chunk,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContentDeltaChunks_DeltaChunk) Reset() { *m = ContentDeltaChunks_DeltaChunk{} }
func (m *ContentDeltaChunks_DeltaChunk) String() string { return proto.CompactTextString(m) }
func (*ContentDeltaChunks_DeltaChunk) ProtoMessage() {}
func (*ContentDeltaChunks_DeltaChunk) Descriptor() ([]byte, []int) {
return fileDescriptor_e3cda137a29253ba, []int{3, 0}
}
func (m *ContentDeltaChunks_DeltaChunk) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Unmarshal(m, b)
}
func (m *ContentDeltaChunks_DeltaChunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Marshal(b, m, deterministic)
}
func (m *ContentDeltaChunks_DeltaChunk) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Merge(m, src)
}
func (m *ContentDeltaChunks_DeltaChunk) XXX_Size() int {
return xxx_messageInfo_ContentDeltaChunks_DeltaChunk.Size(m)
}
func (m *ContentDeltaChunks_DeltaChunk) XXX_DiscardUnknown() {
xxx_messageInfo_ContentDeltaChunks_DeltaChunk.DiscardUnknown(m)
}
var xxx_messageInfo_ContentDeltaChunks_DeltaChunk proto.InternalMessageInfo
func (m *ContentDeltaChunks_DeltaChunk) GetShaSource() []byte {
if m != nil {
return m.ShaSource
}
return nil
}
func (m *ContentDeltaChunks_DeltaChunk) GetShaTarget() []byte {
if m != nil {
return m.ShaTarget
}
return nil
}
func (m *ContentDeltaChunks_DeltaChunk) GetSizeOriginal() uint32 {
if m != nil && m.SizeOriginal != nil {
return *m.SizeOriginal
}
return 0
}
func (m *ContentDeltaChunks_DeltaChunk) GetPatchMethod() uint32 {
if m != nil && m.PatchMethod != nil {
return *m.PatchMethod
}
return 0
}
func (m *ContentDeltaChunks_DeltaChunk) GetChunk() []byte {
if m != nil {
return m.Chunk
}
return nil
}
func init() {
proto.RegisterType((*ContentManifestPayload)(nil), "ContentManifestPayload")
proto.RegisterType((*ContentManifestPayload_FileMapping)(nil), "ContentManifestPayload.FileMapping")
proto.RegisterType((*ContentManifestPayload_FileMapping_ChunkData)(nil), "ContentManifestPayload.FileMapping.ChunkData")
proto.RegisterType((*ContentManifestMetadata)(nil), "ContentManifestMetadata")
proto.RegisterType((*ContentManifestSignature)(nil), "ContentManifestSignature")
proto.RegisterType((*ContentDeltaChunks)(nil), "ContentDeltaChunks")
proto.RegisterType((*ContentDeltaChunks_DeltaChunk)(nil), "ContentDeltaChunks.DeltaChunk")
}
var content_manifest_fileDescriptor0 = []byte{
// 409 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x91, 0xbd, 0x8e, 0xd4, 0x30,
0x14, 0x85, 0xc9, 0xfc, 0x26, 0x37, 0x09, 0x5a, 0xbc, 0xb0, 0x58, 0x43, 0x83, 0x96, 0x66, 0x9b,
0x4d, 0x81, 0x44, 0x49, 0xc3, 0x22, 0x44, 0x33, 0x12, 0x12, 0x0f, 0x10, 0x5d, 0x1c, 0x27, 0x6b,
0x4d, 0x62, 0x07, 0xdb, 0x29, 0x96, 0x8a, 0x17, 0xe1, 0x0d, 0x91, 0x78, 0x05, 0x6c, 0x27, 0x99,
0x1d, 0x8d, 0x28, 0x28, 0xcf, 0xf1, 0xb5, 0xcf, 0x77, 0x8f, 0xe1, 0x8a, 0x29, 0x69, 0xb9, 0xb4,
0x65, 0x87, 0x52, 0xd4, 0xdc, 0xd8, 0xa2, 0xd7, 0xca, 0xaa, 0xeb, 0x3f, 0x0b, 0xb8, 0xba, 0x1b,
0x8f, 0xf6, 0xd3, 0xc9, 0x17, 0x7c, 0x68, 0x15, 0x56, 0xe4, 0x1d, 0xc4, 0x1d, 0xf6, 0xbd, 0x90,
0x8d, 0xa1, 0xd1, 0xeb, 0xe5, 0x4d, 0xfa, 0xf6, 0x4d, 0xf1, 0xef, 0xd1, 0xe2, 0x93, 0x68, 0xf9,
0x7e, 0x9c, 0xdd, 0xfd, 0x5a, 0x40, 0x7a, 0xa2, 0xc9, 0x05, 0xc4, 0xb5, 0x93, 0x12, 0x3b, 0xee,
0x9e, 0x89, 0x6e, 0x12, 0x92, 0xc1, 0xca, 0x88, 0x1f, 0x9c, 0x2e, 0x9c, 0x5a, 0x91, 0x1c, 0xd6,
0x75, 0x8b, 0x2e, 0x63, 0xe9, 0x64, 0x4e, 0x9e, 0x43, 0x66, 0xee, 0xb1, 0x3c, 0x5e, 0x59, 0x39,
0x37, 0x23, 0x97, 0x90, 0x7a, 0x77, 0x5a, 0x82, 0xae, 0x83, 0xf9, 0x1e, 0x36, 0xec, 0x7e, 0x90,
0x07, 0x43, 0x37, 0x01, 0xef, 0xf6, 0x3f, 0xf0, 0x8a, 0x3b, 0x7f, 0xe3, 0x23, 0x5a, 0x24, 0x04,
0xa0, 0x15, 0xf2, 0x60, 0x51, 0x37, 0xdc, 0xd2, 0xad, 0x47, 0xdb, 0x21, 0x24, 0x8f, 0x03, 0x29,
0x2c, 0x5d, 0x68, 0x80, 0xce, 0xbc, 0x60, 0x9a, 0x05, 0xe6, 0x2d, 0x79, 0x0a, 0x1b, 0x55, 0xd7,
0xc6, 0x5d, 0x5b, 0x86, 0x1d, 0x1c, 0x1e, 0xfb, 0x56, 0x2a, 0x2d, 0x1a, 0x21, 0xb1, 0x0d, 0xcc,
0x39, 0x79, 0x01, 0xb9, 0x33, 0x99, 0xea, 0x7a, 0xcd, 0x8d, 0xe1, 0x55, 0xa0, 0xce, 0xaf, 0x7f,
0x47, 0xf0, 0xf2, 0x8c, 0x73, 0xcf, 0x2d, 0x56, 0x3e, 0xd1, 0x75, 0x55, 0xf1, 0x5e, 0xd9, 0x52,
0x54, 0x21, 0x36, 0xd4, 0xd1, 0x88, 0xea, 0xf8, 0x6b, 0x53, 0x67, 0xfe, 0x69, 0xcd, 0xd1, 0x0a,
0x25, 0x4b, 0x2b, 0x5c, 0x4b, 0x63, 0x77, 0xaf, 0xe0, 0x72, 0xee, 0xcd, 0x94, 0x5c, 0x32, 0xfd,
0xd0, 0x5b, 0x97, 0xeb, 0x71, 0x62, 0x42, 0xe1, 0xc2, 0xe1, 0x54, 0xc2, 0x1c, 0x1e, 0x41, 0xd7,
0xe1, 0xb5, 0x1d, 0x90, 0xf9, 0xe4, 0x84, 0x76, 0x33, 0x27, 0x0d, 0x52, 0x7c, 0x1f, 0x78, 0x39,
0x55, 0xbd, 0x3d, 0xee, 0xa6, 0xd9, 0x49, 0x46, 0x1c, 0xec, 0x67, 0x90, 0x78, 0x9b, 0xb5, 0x1c,
0x35, 0x4d, 0xc2, 0xba, 0xb7, 0x40, 0xcf, 0xb6, 0xfd, 0x2a, 0x1a, 0x89, 0x76, 0xd0, 0xdc, 0x8f,
0x9b, 0x59, 0x8c, 0x35, 0x7f, 0x58, 0x7f, 0x8e, 0x7e, 0x46, 0x4f, 0xfe, 0x06, 0x00, 0x00, 0xff,
0xff, 0xc6, 0x87, 0xdb, 0xe6, 0xaf, 0x02, 0x00, 0x00,
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,
}

File diff suppressed because it is too large Load Diff

View File

@@ -723,7 +723,7 @@ func (d *MsgClientNewLoginKeyAccepted) Deserialize(r io.Reader) error {
const (
MsgClientLogon_ObfuscationMask uint32 = 0xBAADF00D
MsgClientLogon_CurrentProtocol uint32 = 65579
MsgClientLogon_CurrentProtocol uint32 = 65580
MsgClientLogon_ProtocolVerMajorMask uint32 = 0xFFFF0000
MsgClientLogon_ProtocolVerMinorMask uint32 = 0xFFFF
MsgClientLogon_ProtocolVerMinorMinGameServers uint16 = 4
@@ -744,7 +744,11 @@ const (
MsgClientLogon_ProtocolVerMinorMinForMachineAuth uint16 = 33
MsgClientLogon_ProtocolVerMinorMinForSessionIDLastAnon uint16 = 36
MsgClientLogon_ProtocolVerMinorMinForEnhancedAppList uint16 = 40
MsgClientLogon_ProtocolVerMinorMinForSteamGuardNotificationUI uint16 = 41
MsgClientLogon_ProtocolVerMinorMinForProtoBufServiceModuleCalls uint16 = 42
MsgClientLogon_ProtocolVerMinorMinForGzipMultiMessages uint16 = 43
MsgClientLogon_ProtocolVerMinorMinForNewVoiceCallAuthorize uint16 = 44
MsgClientLogon_ProtocolVerMinorMinForClientInstanceIDs uint16 = 44
)
type MsgClientLogon struct {
@@ -1976,64 +1980,6 @@ func (d *MsgClientChatRoomInfo) Deserialize(r io.Reader) error {
return err
}
type MsgClientGetNumberOfCurrentPlayers struct {
GameID uint64
}
func NewMsgClientGetNumberOfCurrentPlayers() *MsgClientGetNumberOfCurrentPlayers {
return &MsgClientGetNumberOfCurrentPlayers{}
}
func (d *MsgClientGetNumberOfCurrentPlayers) GetEMsg() EMsg {
return EMsg_ClientGetNumberOfCurrentPlayers
}
func (d *MsgClientGetNumberOfCurrentPlayers) Serialize(w io.Writer) error {
var err error
err = binary.Write(w, binary.LittleEndian, d.GameID)
return err
}
func (d *MsgClientGetNumberOfCurrentPlayers) Deserialize(r io.Reader) error {
var err error
d.GameID, err = rwu.ReadUint64(r)
return err
}
type MsgClientGetNumberOfCurrentPlayersResponse struct {
Result EResult
NumPlayers uint32
}
func NewMsgClientGetNumberOfCurrentPlayersResponse() *MsgClientGetNumberOfCurrentPlayersResponse {
return &MsgClientGetNumberOfCurrentPlayersResponse{}
}
func (d *MsgClientGetNumberOfCurrentPlayersResponse) GetEMsg() EMsg {
return EMsg_ClientGetNumberOfCurrentPlayersResponse
}
func (d *MsgClientGetNumberOfCurrentPlayersResponse) Serialize(w io.Writer) error {
var err error
err = binary.Write(w, binary.LittleEndian, d.Result)
if err != nil {
return err
}
err = binary.Write(w, binary.LittleEndian, d.NumPlayers)
return err
}
func (d *MsgClientGetNumberOfCurrentPlayersResponse) Deserialize(r io.Reader) error {
var err error
t0, err := rwu.ReadInt32(r)
if err != nil {
return err
}
d.Result = EResult(t0)
d.NumPlayers, err = rwu.ReadUint32(r)
return err
}
type MsgClientSetIgnoreFriend struct {
MySteamId steamid.SteamId
SteamIdFriend steamid.SteamId
@@ -2079,8 +2025,8 @@ func (d *MsgClientSetIgnoreFriend) Deserialize(r io.Reader) error {
}
type MsgClientSetIgnoreFriendResponse struct {
Unknown uint64
Result EResult
FriendId steamid.SteamId
Result EResult
}
func NewMsgClientSetIgnoreFriendResponse() *MsgClientSetIgnoreFriendResponse {
@@ -2093,7 +2039,7 @@ func (d *MsgClientSetIgnoreFriendResponse) GetEMsg() EMsg {
func (d *MsgClientSetIgnoreFriendResponse) Serialize(w io.Writer) error {
var err error
err = binary.Write(w, binary.LittleEndian, d.Unknown)
err = binary.Write(w, binary.LittleEndian, d.FriendId)
if err != nil {
return err
}
@@ -2103,12 +2049,13 @@ func (d *MsgClientSetIgnoreFriendResponse) Serialize(w io.Writer) error {
func (d *MsgClientSetIgnoreFriendResponse) Deserialize(r io.Reader) error {
var err error
d.Unknown, err = rwu.ReadUint64(r)
t0, err := rwu.ReadUint64(r)
if err != nil {
return err
}
t0, err := rwu.ReadInt32(r)
d.Result = EResult(t0)
d.FriendId = steamid.SteamId(t0)
t1, err := rwu.ReadInt32(r)
d.Result = EResult(t1)
return err
}
@@ -2226,73 +2173,6 @@ func (d *MsgClientLogOnResponse) Deserialize(r io.Reader) error {
return err
}
type MsgClientSendGuestPass struct {
GiftId uint64
GiftType uint8
AccountId uint32
}
func NewMsgClientSendGuestPass() *MsgClientSendGuestPass {
return &MsgClientSendGuestPass{}
}
func (d *MsgClientSendGuestPass) GetEMsg() EMsg {
return EMsg_ClientSendGuestPass
}
func (d *MsgClientSendGuestPass) Serialize(w io.Writer) error {
var err error
err = binary.Write(w, binary.LittleEndian, d.GiftId)
if err != nil {
return err
}
err = binary.Write(w, binary.LittleEndian, d.GiftType)
if err != nil {
return err
}
err = binary.Write(w, binary.LittleEndian, d.AccountId)
return err
}
func (d *MsgClientSendGuestPass) Deserialize(r io.Reader) error {
var err error
d.GiftId, err = rwu.ReadUint64(r)
if err != nil {
return err
}
d.GiftType, err = rwu.ReadUint8(r)
if err != nil {
return err
}
d.AccountId, err = rwu.ReadUint32(r)
return err
}
type MsgClientSendGuestPassResponse struct {
Result EResult
}
func NewMsgClientSendGuestPassResponse() *MsgClientSendGuestPassResponse {
return &MsgClientSendGuestPassResponse{}
}
func (d *MsgClientSendGuestPassResponse) GetEMsg() EMsg {
return EMsg_ClientSendGuestPassResponse
}
func (d *MsgClientSendGuestPassResponse) Serialize(w io.Writer) error {
var err error
err = binary.Write(w, binary.LittleEndian, d.Result)
return err
}
func (d *MsgClientSendGuestPassResponse) Deserialize(r io.Reader) error {
var err error
t0, err := rwu.ReadInt32(r)
d.Result = EResult(t0)
return err
}
type MsgClientServerUnavailable struct {
JobidSent uint64
EMsgSent uint32

View File

@@ -8,135 +8,116 @@ import (
)
// CMServers contains a list of worlwide servers
var CMServers = [][]string{
{ // North American Servers
// Chicago
"162.254.193.44:27018",
"162.254.193.44:27019",
"162.254.193.44:27020",
"162.254.193.44:27021",
"162.254.193.45:27017",
"162.254.193.45:27018",
"162.254.193.45:27019",
"162.254.193.45:27021",
"162.254.193.46:27017",
"162.254.193.46:27018",
"162.254.193.46:27019",
"162.254.193.46:27020",
"162.254.193.46:27021",
"162.254.193.47:27019",
"162.254.193.47:27020",
// Ashburn
"208.78.164.9:27017",
"208.78.164.9:27018",
"208.78.164.9:27019",
"208.78.164.10:27017",
"208.78.164.10:27018",
"208.78.164.10:27019",
"208.78.164.11:27017",
"208.78.164.11:27018",
"208.78.164.11:27019",
"208.78.164.12:27017",
"208.78.164.12:27018",
"208.78.164.12:27019",
"208.78.164.13:27017",
"208.78.164.13:27018",
"208.78.164.13:27019",
"208.78.164.14:27017",
"208.78.164.14:27018",
"208.78.164.14:27019",
},
{ // Europe Servers
// Luxembourg
"146.66.152.10:27017",
"146.66.152.10:27018",
"146.66.152.10:27019",
"146.66.152.10:27020",
"146.66.152.11:27017",
"146.66.152.11:27018",
"146.66.152.11:27019",
"146.66.152.11:27020",
// Poland
"155.133.242.8:27017",
"155.133.242.8:27018",
"155.133.242.8:27019",
"155.133.242.8:27020",
"155.133.242.9:27017",
"155.133.242.9:27018",
"155.133.242.9:27019",
"155.133.242.9:27020",
// Vienna
"146.66.155.8:27017",
"146.66.155.8:27018",
"146.66.155.8:27019",
"146.66.155.8:27020",
"185.25.182.10:27017",
"185.25.182.10:27018",
"185.25.182.10:27019",
"185.25.182.10:27020",
// London
"162.254.196.40:27017",
"162.254.196.40:27018",
"162.254.196.40:27019",
"162.254.196.40:27020",
"162.254.196.40:27021",
"162.254.196.41:27017",
"162.254.196.41:27018",
"162.254.196.41:27019",
"162.254.196.41:27020",
"162.254.196.41:27021",
"162.254.196.42:27017",
"162.254.196.42:27018",
"162.254.196.42:27019",
"162.254.196.42:27020",
"162.254.196.42:27021",
"162.254.196.43:27017",
"162.254.196.43:27018",
"162.254.196.43:27019",
"162.254.196.43:27020",
"162.254.196.43:27021",
// Stockholm
"185.25.180.14:27017",
"185.25.180.14:27018",
"185.25.180.14:27019",
"185.25.180.14:27020",
"185.25.180.15:27017",
"185.25.180.15:27018",
"185.25.180.15:27019",
"185.25.180.15:27020",
},
var CMServers = []string{
"155.133.248.52:27018",
"162.254.197.40:27018",
"162.254.197.180:27019",
"155.133.248.50:27019",
"162.254.197.181:27017",
"162.254.197.42:27019",
"162.254.197.180:27017",
"162.254.197.181:27018",
"162.254.197.42:27018",
"155.133.248.50:27017",
"155.133.248.52:27019",
"155.133.248.51:27019",
"155.133.248.53:27019",
"155.133.248.51:27017",
"155.133.248.53:27017",
"155.133.248.52:27017",
"155.133.248.50:27018",
"162.254.197.180:27018",
"162.254.197.40:27017",
"162.254.197.40:27019",
"162.254.197.42:27017",
"162.254.197.181:27019",
"155.133.248.53:27018",
"155.133.248.51:27018",
"146.66.152.11:27017",
"146.66.152.10:27019",
"146.66.152.10:27017",
"146.66.152.10:27018",
"146.66.152.11:27019",
"162.254.198.133:27017",
"162.254.198.133:27018",
"162.254.198.130:27019",
"162.254.198.130:27017",
"162.254.198.132:27018",
"162.254.198.130:27018",
"162.254.198.132:27017",
"162.254.198.132:27019",
"162.254.198.131:27019",
"162.254.198.131:27017",
"146.66.152.11:27018",
"162.254.198.131:27018",
"162.254.198.133:27019",
"185.25.182.77:27017",
"185.25.182.76:27018",
"185.25.182.76:27019",
"185.25.182.77:27018",
"185.25.182.76:27017",
"185.25.182.77:27019",
"162.254.196.67:27019",
"162.254.196.67:27018",
"162.254.196.83:27018",
"162.254.196.84:27018",
"162.254.196.83:27017",
"162.254.196.84:27017",
"162.254.196.68:27019",
"162.254.196.68:27017",
"162.254.196.84:27019",
"162.254.196.67:27017",
"162.254.196.83:27019",
"162.254.196.68:27018",
"146.66.155.101:27017",
"146.66.155.101:27018",
"146.66.155.100:27017",
"146.66.155.100:27018",
"146.66.155.101:27019",
"146.66.155.100:27019",
"155.133.230.50:27017",
"155.133.230.34:27018",
"155.133.230.34:27017",
"155.133.230.50:27019",
"155.133.230.34:27019",
"155.133.230.50:27018",
"162.254.192.100:27017",
"162.254.192.108:27017",
"155.133.246.68:27017",
"155.133.246.68:27018",
"155.133.246.68:27019",
"155.133.246.69:27019",
"155.133.246.69:27017",
"155.133.246.69:27018",
"162.254.192.108:27018",
"162.254.192.101:27018",
"162.254.192.101:27019",
"162.254.192.109:27018",
"162.254.192.100:27018",
"162.254.192.109:27017",
"162.254.192.109:27019",
"162.254.192.108:27019",
"162.254.192.101:27017",
"162.254.192.100:27019",
"162.254.193.46:27019",
"162.254.193.6:27018",
"162.254.193.47:27018",
"162.254.193.6:27019",
"162.254.193.7:27018",
"162.254.193.7:27017",
"162.254.193.7:27019",
"162.254.193.47:27017",
"162.254.193.47:27019",
"162.254.193.46:27018",
}
// GetRandomCM returns back a random server anywhere
// GetRandomCM returns a random server from a built-in IP list.
//
// Prefer Client.Connect(), which uses IPs from the Steam Directory,
// which is always more up-to-date.
func GetRandomCM() *netutil.PortAddr {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
servers := append(CMServers[0], CMServers[1]...)
addr := netutil.ParsePortAddr(servers[rng.Int31n(int32(len(servers)))])
if addr == nil {
panic("invalid address in CMServers slice")
}
return addr
}
// GetRandomNorthAmericaCM returns back a random server in north america
func GetRandomNorthAmericaCM() *netutil.PortAddr {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
addr := netutil.ParsePortAddr(CMServers[0][rng.Int31n(int32(len(CMServers[0])))])
if addr == nil {
panic("invalid address in CMServers slice")
}
return addr
}
// GetRandomEuropeCM returns back a random server in europe
func GetRandomEuropeCM() *netutil.PortAddr {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
addr := netutil.ParsePortAddr(CMServers[1][rng.Int31n(int32(len(CMServers[1])))])
addr := netutil.ParsePortAddr(CMServers[rng.Int31n(int32(len(CMServers)))])
if addr == nil {
panic("invalid address in CMServers slice")
}

View File

@@ -3,7 +3,10 @@ package steam
import (
"bytes"
"encoding/binary"
"encoding/hex"
"io"
"sync"
"time"
. "github.com/Philipp15b/go-steam/protocol"
. "github.com/Philipp15b/go-steam/protocol/protobuf"
. "github.com/Philipp15b/go-steam/protocol/steamlang"
@@ -11,9 +14,6 @@ import (
"github.com/Philipp15b/go-steam/socialcache"
. "github.com/Philipp15b/go-steam/steamid"
"github.com/golang/protobuf/proto"
"io"
"sync"
"time"
)
// Provides access to social aspects of Steam.
@@ -21,7 +21,7 @@ type Social struct {
mutex sync.RWMutex
name string
avatar string
avatar []byte
personaState EPersonaState
Friends *socialcache.FriendsList
@@ -41,7 +41,7 @@ func newSocial(client *Client) *Social {
}
// Gets the local user's avatar
func (s *Social) GetAvatar() string {
func (s *Social) GetAvatar() []byte {
s.mutex.RLock()
defer s.mutex.RUnlock()
return s.avatar
@@ -156,7 +156,7 @@ func (s *Social) RequestProfileInfo(id SteamId) {
// Requests all offline messages and marks them as read
func (s *Social) RequestOfflineMessages() {
s.client.Write(NewClientMsgProtobuf(EMsg_ClientFSGetFriendMessageHistoryForOfflineMessages, &CMsgClientFSGetFriendMessageHistoryForOfflineMessages{}))
s.client.Write(NewClientMsgProtobuf(EMsg_ClientChatGetFriendMessageHistoryForOfflineMessages, &CMsgClientChatGetFriendMessageHistoryForOfflineMessages{}))
}
// Attempts to join a chat room
@@ -307,7 +307,7 @@ func (s *Social) handlePersonaState(packet *Packet) {
if friend.GetPlayerName() != "" {
s.name = friend.GetPlayerName()
}
avatar := hex.EncodeToString(friend.GetAvatarHash())
avatar := friend.GetAvatarHash()
if ValidAvatar(avatar) {
s.avatar = avatar
}
@@ -319,7 +319,7 @@ func (s *Social) handlePersonaState(packet *Packet) {
}
}
if (flags & EClientPersonaStateFlag_Presence) == EClientPersonaStateFlag_Presence {
avatar := hex.EncodeToString(friend.GetAvatarHash())
avatar := friend.GetAvatarHash()
if ValidAvatar(avatar) {
s.Friends.SetAvatar(id, avatar)
}
@@ -338,7 +338,7 @@ func (s *Social) handlePersonaState(packet *Packet) {
}
}
if (flags & EClientPersonaStateFlag_Presence) == EClientPersonaStateFlag_Presence {
avatar := hex.EncodeToString(friend.GetAvatarHash())
avatar := friend.GetAvatarHash()
if ValidAvatar(avatar) {
s.Groups.SetAvatar(id, avatar)
}
@@ -358,7 +358,7 @@ func (s *Social) handlePersonaState(packet *Packet) {
SourceSteamId: SteamId(friend.GetSteamidSource()),
GameDataBlob: friend.GetGameDataBlob(),
Name: friend.GetPlayerName(),
Avatar: hex.EncodeToString(friend.GetAvatarHash()),
Avatar: friend.GetAvatarHash(),
LastLogOff: friend.GetLastLogoff(),
LastLogOn: friend.GetLastLogon(),
ClanRank: friend.GetClanRank(),
@@ -366,8 +366,6 @@ func (s *Social) handlePersonaState(packet *Packet) {
OnlineSessionInstances: friend.GetOnlineSessionInstances(),
PublishedSessionId: friend.GetPublishedInstanceId(),
PersonaSetByUser: friend.GetPersonaSetByUser(),
FacebookName: friend.GetFacebookName(),
FacebookId: friend.GetFacebookId(),
})
}
}
@@ -376,10 +374,10 @@ func (s *Social) handleClanState(packet *Packet) {
body := new(CMsgClientClanState)
packet.ReadProtoMsg(body)
var name string
var avatar string
var avatar []byte
if body.GetNameInfo() != nil {
name = body.GetNameInfo().GetClanName()
avatar = hex.EncodeToString(body.GetNameInfo().GetShaAvatar())
avatar = body.GetNameInfo().GetShaAvatar()
}
var totalCount, onlineCount, chattingCount, ingameCount uint32
if body.GetUserCounts() != nil {
@@ -408,18 +406,13 @@ func (s *Social) handleClanState(packet *Packet) {
JustPosted: announce.GetJustPosted(),
})
}
flags := EClientPersonaStateFlag(body.GetMUnStatusFlags())
//Add stuff to group
clanid := SteamId(body.GetSteamidClan())
if (flags & EClientPersonaStateFlag_PlayerName) == EClientPersonaStateFlag_PlayerName {
if name != "" {
s.Groups.SetName(clanid, name)
}
}
if (flags & EClientPersonaStateFlag_Presence) == EClientPersonaStateFlag_Presence {
if ValidAvatar(avatar) {
s.Groups.SetAvatar(clanid, avatar)
}
if body.NameInfo != nil {
info := body.NameInfo
s.Groups.SetName(clanid, info.GetClanName())
s.Groups.SetAvatar(clanid, info.GetShaAvatar())
}
if body.GetUserCounts() != nil {
s.Groups.SetMemberTotalCount(clanid, totalCount)
@@ -428,8 +421,7 @@ func (s *Social) handleClanState(packet *Packet) {
s.Groups.SetMemberInGameCount(clanid, ingameCount)
}
s.client.Emit(&ClanStateEvent{
ClandId: clanid,
StateFlags: EClientPersonaStateFlag(body.GetMUnStatusFlags()),
ClanId: clanid,
AccountFlags: EAccountFlags(body.GetClanAccountFlags()),
ClanName: name,
Avatar: avatar,
@@ -606,7 +598,7 @@ func (s *Social) handleProfileInfoResponse(packet *Packet) {
}
func (s *Social) handleFriendMessageHistoryResponse(packet *Packet) {
body := new(CMsgClientFSGetFriendMessageHistoryResponse)
body := new(CMsgClientChatGetFriendMessageHistoryResponse)
packet.ReadProtoMsg(body)
steamid := SteamId(body.GetSteamid())
for _, message := range body.GetMessages() {

View File

@@ -1,9 +1,10 @@
package steam
import (
"time"
. "github.com/Philipp15b/go-steam/protocol/steamlang"
. "github.com/Philipp15b/go-steam/steamid"
"time"
)
type FriendsListEvent struct{}
@@ -41,7 +42,7 @@ type PersonaStateEvent struct {
SourceSteamId SteamId `json:",string"`
GameDataBlob []byte
Name string
Avatar string
Avatar []byte
LastLogOff uint32
LastLogOn uint32
ClanRank uint32
@@ -49,17 +50,14 @@ type PersonaStateEvent struct {
OnlineSessionInstances uint32
PublishedSessionId uint32
PersonaSetByUser bool
FacebookName string
FacebookId uint64 `json:",string"`
}
// Fired when a clan's state has been changed
type ClanStateEvent struct {
ClandId SteamId `json:",string"`
StateFlags EClientPersonaStateFlag
ClanId SteamId `json:",string"`
AccountFlags EAccountFlags
ClanName string
Avatar string
Avatar []byte
MemberTotalCount uint32
MemberOnlineCount uint32
MemberChattingCount uint32

View File

@@ -2,9 +2,10 @@ package socialcache
import (
"errors"
"sync"
. "github.com/Philipp15b/go-steam/protocol/steamlang"
. "github.com/Philipp15b/go-steam/steamid"
"sync"
)
// Friends list is a thread safe map
@@ -76,7 +77,7 @@ func (list *FriendsList) SetName(id SteamId, name string) {
}
}
func (list *FriendsList) SetAvatar(id SteamId, hash string) {
func (list *FriendsList) SetAvatar(id SteamId, hash []byte) {
list.mutex.Lock()
defer list.mutex.Unlock()
if val, ok := list.byId[id]; ok {
@@ -136,7 +137,7 @@ func (list *FriendsList) SetGameName(id SteamId, name string) {
type Friend struct {
SteamId SteamId `json:",string"`
Name string
Avatar string
Avatar []byte
Relationship EFriendRelationship
PersonaState EPersonaState
PersonaStateFlags EPersonaStateFlag

View File

@@ -2,9 +2,10 @@ package socialcache
import (
"errors"
"sync"
. "github.com/Philipp15b/go-steam/protocol/steamlang"
. "github.com/Philipp15b/go-steam/steamid"
"sync"
)
// Groups list is a thread safe map
@@ -78,7 +79,7 @@ func (list *GroupsList) SetName(id SteamId, name string) {
}
}
func (list *GroupsList) SetAvatar(id SteamId, hash string) {
func (list *GroupsList) SetAvatar(id SteamId, hash []byte) {
list.mutex.Lock()
defer list.mutex.Unlock()
id = id.ChatToClan()
@@ -136,7 +137,7 @@ func (list *GroupsList) SetMemberInGameCount(id SteamId, count uint32) {
type Group struct {
SteamId SteamId `json:",string"`
Name string
Avatar string
Avatar []byte
Relationship EClanRelationship
MemberTotalCount uint32
MemberOnlineCount uint32

3
vendor/github.com/Rhymen/go-whatsapp/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea/
docs/
build/

131
vendor/github.com/Rhymen/go-whatsapp/README.md generated vendored Normal file
View File

@@ -0,0 +1,131 @@
# go-whatsapp
Package rhymen/go-whatsapp implements the WhatsApp Web API to provide a clean interface for developers. Big thanks to all contributors of the [sigalor/whatsapp-web-reveng](https://github.com/sigalor/whatsapp-web-reveng) project. The official WhatsApp Business API was released in August 2018. You can check it out [here](https://www.whatsapp.com/business/api).
## Installation
```sh
go get github.com/Rhymen/go-whatsapp
```
## Usage
### Creating a connection
```go
import (
whatsapp "github.com/Rhymen/go-whatsapp"
)
wac, err := whatsapp.NewConn(20 * time.Second)
```
The duration passed to the NewConn function is used to timeout login requests. If you have a bad internet connection use a higher timeout value. This function only creates a websocket connection, it does not handle authentication.
### Login
```go
qrChan := make(chan string)
go func() {
fmt.Printf("qr code: %v\n", <-qrChan)
//show qr code or save it somewhere to scan
}()
sess, err := wac.Login(qrChan)
```
The authentication process requires you to scan the qr code, that is send through the channel, with the device you are using whatsapp on. The session struct that is returned can be saved and used to restore the login without scanning the qr code again. The qr code has a ttl of 20 seconds and the login function throws a timeout err if the time has passed or any other request fails.
### Restore
```go
newSess, err := wac.RestoreWithSession(sess)
```
The restore function needs a valid session and returns the new session that was created.
### Add message handlers
```go
type myHandler struct{}
func (myHandler) HandleError(err error) {
fmt.Fprintf(os.Stderr, "%v", err)
}
func (myHandler) HandleTextMessage(message whatsapp.TextMessage) {
fmt.Println(message)
}
func (myHandler) HandleImageMessage(message whatsapp.ImageMessage) {
fmt.Println(message)
}
func (myHandler) HandleDocumentMessage(message whatsapp.DocumentMessage) {
fmt.Println(message)
}
func (myHandler) HandleVideoMessage(message whatsapp.VideoMessage) {
fmt.Println(message)
}
func (myHandler) HandleAudioMessage(message whatsapp.AudioMessage){
fmt.Println(message)
}
func (myHandler) HandleJsonMessage(message string) {
fmt.Println(message)
}
func (myHandler) HandleContactMessage(message whatsapp.ContactMessage) {
fmt.Println(message)
}
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.
### Sending text messages
```go
text := whatsapp.TextMessage{
Info: whatsapp.MessageInfo{
RemoteJid: "0123456789@s.whatsapp.net",
},
Text: "Hello Whatsapp",
}
err := wac.Send(text)
```
### Sending Contact Messages
```go
contactMessage := whatsapp.ContactMessage{
Info: whatsapp.MessageInfo{
RemoteJid: "0123456789@s.whatsapp.net",
},
DisplayName: "Luke Skylwallker",
Vcard: "BEGIN:VCARD\nVERSION:3.0\nN:Skyllwalker;Luke;;\nFN:Luke Skywallker\nitem1.TEL;waid=0123456789:+1 23 456789789\nitem1.X-ABLabel:Mobile\nEND:VCARD",
}
id, error := client.WaConn.Send(contactMessage)
```
The message will be send over the websocket. The attributes seen above are the required ones. All other relevant attributes (id, timestamp, fromMe, status) are set if they are missing in the struct. For the time being we only support text messages, but other types are planned for the near future.
## Legal
This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its
affiliates or subsidiaries. This is an independent and unofficial software. Use at your own risk.
## License
The MIT License (MIT)
Copyright (c) 2018
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.

View File

@@ -2,7 +2,7 @@ package binary
import (
"fmt"
"github.com/matterbridge/go-whatsapp/binary/token"
"github.com/Rhymen/go-whatsapp/binary/token"
"io"
"strconv"
)

View File

@@ -2,7 +2,7 @@ package binary
import (
"fmt"
"github.com/matterbridge/go-whatsapp/binary/token"
"github.com/Rhymen/go-whatsapp/binary/token"
"math"
"strconv"
"strings"

106
vendor/github.com/Rhymen/go-whatsapp/binary/node.go generated vendored Normal file
View File

@@ -0,0 +1,106 @@
package binary
import (
"fmt"
pb "github.com/Rhymen/go-whatsapp/binary/proto"
"github.com/golang/protobuf/proto"
)
type Node struct {
Description string
Attributes map[string]string
Content interface{}
}
func Marshal(n Node) ([]byte, error) {
if n.Attributes != nil && n.Content != nil {
a, err := marshalMessageArray(n.Content.([]interface{}))
if err != nil {
return nil, err
}
n.Content = a
}
w := NewEncoder()
if err := w.WriteNode(n); err != nil {
return nil, err
}
return w.GetData(), nil
}
func marshalMessageArray(messages []interface{}) ([]Node, error) {
ret := make([]Node, len(messages))
for i, m := range messages {
if wmi, ok := m.(*pb.WebMessageInfo); ok {
b, err := marshalWebMessageInfo(wmi)
if err != nil {
return nil, nil
}
ret[i] = Node{"message", nil, b}
} else {
ret[i], ok = m.(Node)
if !ok {
return nil, fmt.Errorf("invalid Node")
}
}
}
return ret, nil
}
func marshalWebMessageInfo(p *pb.WebMessageInfo) ([]byte, error) {
b, err := proto.Marshal(p)
if err != nil {
return nil, err
}
return b, nil
}
func Unmarshal(data []byte) (*Node, error) {
r := NewDecoder(data)
n, err := r.ReadNode()
if err != nil {
return nil, err
}
if n != nil && n.Attributes != nil && n.Content != nil {
nContent, ok := n.Content.([]Node)
if ok {
n.Content, err = unmarshalMessageArray(nContent)
if err != nil {
return nil, err
}
}
}
return n, nil
}
func unmarshalMessageArray(messages []Node) ([]interface{}, error) {
ret := make([]interface{}, len(messages))
for i, msg := range messages {
if msg.Description == "message" {
info, err := unmarshalWebMessageInfo(msg.Content.([]byte))
if err != nil {
return nil, err
}
ret[i] = info
} else {
ret[i] = msg
}
}
return ret, nil
}
func unmarshalWebMessageInfo(msg []byte) (*pb.WebMessageInfo, error) {
message := &pb.WebMessageInfo{}
err := proto.Unmarshal(msg, message)
if err != nil {
return nil, err
}
return message, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,671 @@
syntax = "proto2";
package proto;
message HydratedQuickReplyButton {
optional string displayText = 1;
optional string id = 2;
}
message HydratedURLButton {
optional string displayText = 1;
optional string url = 2;
}
message HydratedCallButton {
optional string displayText = 1;
optional string phoneNumber = 2;
}
message HydratedTemplateButton {
optional uint32 index = 4;
oneof hydratedButton {
HydratedQuickReplyButton quickReplyButton = 1;
HydratedURLButton urlButton = 2;
HydratedCallButton callButton = 3;
}
}
message QuickReplyButton {
optional HighlyStructuredMessage displayText = 1;
optional string id = 2;
}
message URLButton {
optional HighlyStructuredMessage displayText = 1;
optional HighlyStructuredMessage url = 2;
}
message CallButton {
optional HighlyStructuredMessage displayText = 1;
optional HighlyStructuredMessage phoneNumber = 2;
}
message TemplateButton {
optional uint32 index = 4;
oneof button {
QuickReplyButton quickReplyButton = 1;
URLButton urlButton = 2;
CallButton callButton = 3;
}
}
message Location {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional string name = 3;
}
message Point {
optional double x = 3;
optional double y = 4;
}
message InteractiveAnnotation {
repeated Point polygonVertices = 1;
oneof action {
Location location = 2;
}
}
message AdReplyInfo {
optional string advertiserName = 1;
enum AD_REPLY_INFO_MEDIATYPE {
NONE = 0;
IMAGE = 1;
VIDEO = 2;
}
optional AD_REPLY_INFO_MEDIATYPE mediaType = 2;
optional bytes jpegThumbnail = 16;
optional string caption = 17;
}
message ContextInfo {
optional string stanzaId = 1;
optional string participant = 2;
optional Message quotedMessage = 3;
optional string remoteJid = 4;
repeated string mentionedJid = 15;
optional string conversionSource = 18;
optional bytes conversionData = 19;
optional uint32 conversionDelaySeconds = 20;
optional uint32 forwardingScore = 21;
optional bool isForwarded = 22;
optional AdReplyInfo quotedAd = 23;
optional MessageKey placeholderKey = 24;
optional uint32 expiration = 25;
}
message SenderKeyDistributionMessage {
optional string groupId = 1;
optional bytes axolotlSenderKeyDistributionMessage = 2;
}
message ImageMessage {
optional string url = 1;
optional string mimetype = 2;
optional string caption = 3;
optional bytes fileSha256 = 4;
optional uint64 fileLength = 5;
optional uint32 height = 6;
optional uint32 width = 7;
optional bytes mediaKey = 8;
optional bytes fileEncSha256 = 9;
repeated InteractiveAnnotation interactiveAnnotations = 10;
optional string directPath = 11;
optional int64 mediaKeyTimestamp = 12;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bytes firstScanSidecar = 18;
optional uint32 firstScanLength = 19;
optional uint32 experimentGroupId = 20;
optional bytes scansSidecar = 21;
repeated uint32 scanLengths = 22;
optional bytes midQualityFileSha256 = 23;
optional bytes midQualityFileEncSha256 = 24;
}
message ContactMessage {
optional string displayName = 1;
optional string vcard = 16;
optional ContextInfo contextInfo = 17;
}
message LocationMessage {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional string name = 3;
optional string address = 4;
optional string url = 5;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
message ExtendedTextMessage {
optional string text = 1;
optional string matchedText = 2;
optional string canonicalUrl = 4;
optional string description = 5;
optional string title = 6;
optional fixed32 textArgb = 7;
optional fixed32 backgroundArgb = 8;
enum EXTENDED_TEXT_MESSAGE_FONTTYPE {
SANS_SERIF = 0;
SERIF = 1;
NORICAN_REGULAR = 2;
BRYNDAN_WRITE = 3;
BEBASNEUE_REGULAR = 4;
OSWALD_HEAVY = 5;
}
optional EXTENDED_TEXT_MESSAGE_FONTTYPE font = 9;
enum EXTENDED_TEXT_MESSAGE_PREVIEWTYPE {
NONE = 0;
VIDEO = 1;
}
optional EXTENDED_TEXT_MESSAGE_PREVIEWTYPE previewType = 10;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bool doNotPlayInline = 18;
}
message DocumentMessage {
optional string url = 1;
optional string mimetype = 2;
optional string title = 3;
optional bytes fileSha256 = 4;
optional uint64 fileLength = 5;
optional uint32 pageCount = 6;
optional bytes mediaKey = 7;
optional string fileName = 8;
optional bytes fileEncSha256 = 9;
optional string directPath = 10;
optional int64 mediaKeyTimestamp = 11;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
message AudioMessage {
optional string url = 1;
optional string mimetype = 2;
optional bytes fileSha256 = 3;
optional uint64 fileLength = 4;
optional uint32 seconds = 5;
optional bool ptt = 6;
optional bytes mediaKey = 7;
optional bytes fileEncSha256 = 8;
optional string directPath = 9;
optional int64 mediaKeyTimestamp = 10;
optional ContextInfo contextInfo = 17;
optional bytes streamingSidecar = 18;
}
message VideoMessage {
optional string url = 1;
optional string mimetype = 2;
optional bytes fileSha256 = 3;
optional uint64 fileLength = 4;
optional uint32 seconds = 5;
optional bytes mediaKey = 6;
optional string caption = 7;
optional bool gifPlayback = 8;
optional uint32 height = 9;
optional uint32 width = 10;
optional bytes fileEncSha256 = 11;
repeated InteractiveAnnotation interactiveAnnotations = 12;
optional string directPath = 13;
optional int64 mediaKeyTimestamp = 14;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bytes streamingSidecar = 18;
enum VIDEO_MESSAGE_ATTRIBUTION {
NONE = 0;
GIPHY = 1;
TENOR = 2;
}
optional VIDEO_MESSAGE_ATTRIBUTION gifAttribution = 19;
}
message Call {
optional bytes callKey = 1;
}
message Chat {
optional string displayName = 1;
optional string id = 2;
}
message ProtocolMessage {
optional MessageKey key = 1;
enum PROTOCOL_MESSAGE_TYPE {
REVOKE = 0;
EPHEMERAL_SETTING = 3;
}
optional PROTOCOL_MESSAGE_TYPE type = 2;
optional uint32 ephemeralExpiration = 4;
}
message ContactsArrayMessage {
optional string displayName = 1;
repeated ContactMessage contacts = 2;
optional ContextInfo contextInfo = 17;
}
message HSMCurrency {
optional string currencyCode = 1;
optional int64 amount1000 = 2;
}
message HSMDateTimeComponent {
enum HSM_DATE_TIME_COMPONENT_DAYOFWEEKTYPE {
MONDAY = 1;
TUESDAY = 2;
WEDNESDAY = 3;
THURSDAY = 4;
FRIDAY = 5;
SATURDAY = 6;
SUNDAY = 7;
}
optional HSM_DATE_TIME_COMPONENT_DAYOFWEEKTYPE dayOfWeek = 1;
optional uint32 year = 2;
optional uint32 month = 3;
optional uint32 dayOfMonth = 4;
optional uint32 hour = 5;
optional uint32 minute = 6;
enum HSM_DATE_TIME_COMPONENT_CALENDARTYPE {
GREGORIAN = 1;
SOLAR_HIJRI = 2;
}
optional HSM_DATE_TIME_COMPONENT_CALENDARTYPE calendar = 7;
}
message HSMDateTimeUnixEpoch {
optional int64 timestamp = 1;
}
message HSMDateTime {
oneof datetimeOneof {
HSMDateTimeComponent component = 1;
HSMDateTimeUnixEpoch unixEpoch = 2;
}
}
message HSMLocalizableParameter {
optional string default = 1;
oneof paramOneof {
HSMCurrency currency = 2;
HSMDateTime dateTime = 3;
}
}
message HighlyStructuredMessage {
optional string namespace = 1;
optional string elementName = 2;
repeated string params = 3;
optional string fallbackLg = 4;
optional string fallbackLc = 5;
repeated HSMLocalizableParameter localizableParams = 6;
optional string deterministicLg = 7;
optional string deterministicLc = 8;
optional TemplateMessage hydratedHsm = 9;
}
message SendPaymentMessage {
optional Message noteMessage = 2;
optional MessageKey requestMessageKey = 3;
}
message RequestPaymentMessage {
optional Message noteMessage = 4;
optional string currencyCodeIso4217 = 1;
optional uint64 amount1000 = 2;
optional string requestFrom = 3;
optional int64 expiryTimestamp = 5;
}
message DeclinePaymentRequestMessage {
optional MessageKey key = 1;
}
message CancelPaymentRequestMessage {
optional MessageKey key = 1;
}
message LiveLocationMessage {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional uint32 accuracyInMeters = 3;
optional float speedInMps = 4;
optional uint32 degreesClockwiseFromMagneticNorth = 5;
optional string caption = 6;
optional int64 sequenceNumber = 7;
optional uint32 timeOffset = 8;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
message StickerMessage {
optional string url = 1;
optional bytes fileSha256 = 2;
optional bytes fileEncSha256 = 3;
optional bytes mediaKey = 4;
optional string mimetype = 5;
optional uint32 height = 6;
optional uint32 width = 7;
optional string directPath = 8;
optional uint64 fileLength = 9;
optional int64 mediaKeyTimestamp = 10;
optional uint32 firstFrameLength = 11;
optional bytes firstFrameSidecar = 12;
optional ContextInfo contextInfo = 17;
}
message FourRowTemplate {
optional HighlyStructuredMessage content = 6;
optional HighlyStructuredMessage footer = 7;
repeated TemplateButton buttons = 8;
oneof title {
DocumentMessage documentMessage = 1;
HighlyStructuredMessage highlyStructuredMessage = 2;
ImageMessage imageMessage = 3;
VideoMessage videoMessage = 4;
LocationMessage locationMessage = 5;
}
}
message HydratedFourRowTemplate {
optional string hydratedContentText = 6;
optional string hydratedFooterText = 7;
repeated HydratedTemplateButton hydratedButtons = 8;
optional string templateId = 9;
oneof title {
DocumentMessage documentMessage = 1;
string hydratedTitleText = 2;
ImageMessage imageMessage = 3;
VideoMessage videoMessage = 4;
LocationMessage locationMessage = 5;
}
}
message TemplateMessage {
optional ContextInfo contextInfo = 3;
optional HydratedFourRowTemplate hydratedTemplate = 4;
oneof format {
FourRowTemplate fourRowTemplate = 1;
HydratedFourRowTemplate hydratedFourRowTemplate = 2;
}
}
message TemplateButtonReplyMessage {
optional string selectedId = 1;
optional string selectedDisplayText = 2;
optional ContextInfo contextInfo = 3;
optional uint32 selectedIndex = 4;
}
message ProductSnapshot {
optional ImageMessage productImage = 1;
optional string productId = 2;
optional string title = 3;
optional string description = 4;
optional string currencyCode = 5;
optional int64 priceAmount1000 = 6;
optional string retailerId = 7;
optional string url = 8;
optional uint32 productImageCount = 9;
optional string firstImageId = 11;
}
message ProductMessage {
optional ProductSnapshot product = 1;
optional string businessOwnerJid = 2;
optional ContextInfo contextInfo = 17;
}
message GroupInviteMessage {
optional string groupJid = 1;
optional string inviteCode = 2;
optional int64 inviteExpiration = 3;
optional string groupName = 4;
optional bytes jpegThumbnail = 5;
optional string caption = 6;
optional ContextInfo contextInfo = 7;
}
message DeviceSentMessage {
optional string destinationJid = 1;
optional Message message = 2;
}
message DeviceSyncMessage {
optional bytes serializedXmlBytes = 1;
}
message Message {
optional string conversation = 1;
optional SenderKeyDistributionMessage senderKeyDistributionMessage = 2;
optional ImageMessage imageMessage = 3;
optional ContactMessage contactMessage = 4;
optional LocationMessage locationMessage = 5;
optional ExtendedTextMessage extendedTextMessage = 6;
optional DocumentMessage documentMessage = 7;
optional AudioMessage audioMessage = 8;
optional VideoMessage videoMessage = 9;
optional Call call = 10;
optional Chat chat = 11;
optional ProtocolMessage protocolMessage = 12;
optional ContactsArrayMessage contactsArrayMessage = 13;
optional HighlyStructuredMessage highlyStructuredMessage = 14;
optional SenderKeyDistributionMessage fastRatchetKeySenderKeyDistributionMessage = 15;
optional SendPaymentMessage sendPaymentMessage = 16;
optional LiveLocationMessage liveLocationMessage = 18;
optional RequestPaymentMessage requestPaymentMessage = 22;
optional DeclinePaymentRequestMessage declinePaymentRequestMessage = 23;
optional CancelPaymentRequestMessage cancelPaymentRequestMessage = 24;
optional TemplateMessage templateMessage = 25;
optional StickerMessage stickerMessage = 26;
optional GroupInviteMessage groupInviteMessage = 28;
optional TemplateButtonReplyMessage templateButtonReplyMessage = 29;
optional ProductMessage productMessage = 30;
optional DeviceSentMessage deviceSentMessage = 31;
optional DeviceSyncMessage deviceSyncMessage = 32;
}
message MessageKey {
optional string remoteJid = 1;
optional bool fromMe = 2;
optional string id = 3;
optional string participant = 4;
}
message WebFeatures {
enum WEB_FEATURES_FLAG {
NOT_STARTED = 0;
FORCE_UPGRADE = 1;
DEVELOPMENT = 2;
PRODUCTION = 3;
}
optional WEB_FEATURES_FLAG labelsDisplay = 1;
optional WEB_FEATURES_FLAG voipIndividualOutgoing = 2;
optional WEB_FEATURES_FLAG groupsV3 = 3;
optional WEB_FEATURES_FLAG groupsV3Create = 4;
optional WEB_FEATURES_FLAG changeNumberV2 = 5;
optional WEB_FEATURES_FLAG queryStatusV3Thumbnail = 6;
optional WEB_FEATURES_FLAG liveLocations = 7;
optional WEB_FEATURES_FLAG queryVname = 8;
optional WEB_FEATURES_FLAG voipIndividualIncoming = 9;
optional WEB_FEATURES_FLAG quickRepliesQuery = 10;
optional WEB_FEATURES_FLAG payments = 11;
optional WEB_FEATURES_FLAG stickerPackQuery = 12;
optional WEB_FEATURES_FLAG liveLocationsFinal = 13;
optional WEB_FEATURES_FLAG labelsEdit = 14;
optional WEB_FEATURES_FLAG mediaUpload = 15;
optional WEB_FEATURES_FLAG mediaUploadRichQuickReplies = 18;
optional WEB_FEATURES_FLAG vnameV2 = 19;
optional WEB_FEATURES_FLAG videoPlaybackUrl = 20;
optional WEB_FEATURES_FLAG statusRanking = 21;
optional WEB_FEATURES_FLAG voipIndividualVideo = 22;
optional WEB_FEATURES_FLAG thirdPartyStickers = 23;
optional WEB_FEATURES_FLAG frequentlyForwardedSetting = 24;
optional WEB_FEATURES_FLAG groupsV4JoinPermission = 25;
optional WEB_FEATURES_FLAG recentStickers = 26;
optional WEB_FEATURES_FLAG catalog = 27;
optional WEB_FEATURES_FLAG starredStickers = 28;
optional WEB_FEATURES_FLAG voipGroupCall = 29;
optional WEB_FEATURES_FLAG templateMessage = 30;
optional WEB_FEATURES_FLAG templateMessageInteractivity = 31;
optional WEB_FEATURES_FLAG ephemeralMessages = 32;
}
message TabletNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated NotificationMessageInfo notifyMessage = 5;
}
message NotificationMessageInfo {
optional MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
optional string participant = 4;
}
message WebNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated WebMessageInfo notifyMessages = 5;
}
message PaymentInfo {
optional uint64 amount1000 = 2;
optional string receiverJid = 3;
enum PAYMENT_INFO_STATUS {
UNKNOWN_STATUS = 0;
PROCESSING = 1;
SENT = 2;
NEED_TO_ACCEPT = 3;
COMPLETE = 4;
COULD_NOT_COMPLETE = 5;
REFUNDED = 6;
EXPIRED = 7;
REJECTED = 8;
CANCELLED = 9;
WAITING_FOR_PAYER = 10;
WAITING = 11;
}
optional PAYMENT_INFO_STATUS status = 4;
optional uint64 transactionTimestamp = 5;
optional MessageKey requestMessageKey = 6;
optional uint64 expiryTimestamp = 7;
optional bool futureproofed = 8;
optional string currency = 9;
}
message WebMessageInfo {
required MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
enum WEB_MESSAGE_INFO_STATUS {
ERROR = 0;
PENDING = 1;
SERVER_ACK = 2;
DELIVERY_ACK = 3;
READ = 4;
PLAYED = 5;
}
optional WEB_MESSAGE_INFO_STATUS status = 4;
optional string participant = 5;
optional bool ignore = 16;
optional bool starred = 17;
optional bool broadcast = 18;
optional string pushName = 19;
optional bytes mediaCiphertextSha256 = 20;
optional bool multicast = 21;
optional bool urlText = 22;
optional bool urlNumber = 23;
enum WEB_MESSAGE_INFO_STUBTYPE {
UNKNOWN = 0;
REVOKE = 1;
CIPHERTEXT = 2;
FUTUREPROOF = 3;
NON_VERIFIED_TRANSITION = 4;
UNVERIFIED_TRANSITION = 5;
VERIFIED_TRANSITION = 6;
VERIFIED_LOW_UNKNOWN = 7;
VERIFIED_HIGH = 8;
VERIFIED_INITIAL_UNKNOWN = 9;
VERIFIED_INITIAL_LOW = 10;
VERIFIED_INITIAL_HIGH = 11;
VERIFIED_TRANSITION_ANY_TO_NONE = 12;
VERIFIED_TRANSITION_ANY_TO_HIGH = 13;
VERIFIED_TRANSITION_HIGH_TO_LOW = 14;
VERIFIED_TRANSITION_HIGH_TO_UNKNOWN = 15;
VERIFIED_TRANSITION_UNKNOWN_TO_LOW = 16;
VERIFIED_TRANSITION_LOW_TO_UNKNOWN = 17;
VERIFIED_TRANSITION_NONE_TO_LOW = 18;
VERIFIED_TRANSITION_NONE_TO_UNKNOWN = 19;
GROUP_CREATE = 20;
GROUP_CHANGE_SUBJECT = 21;
GROUP_CHANGE_ICON = 22;
GROUP_CHANGE_INVITE_LINK = 23;
GROUP_CHANGE_DESCRIPTION = 24;
GROUP_CHANGE_RESTRICT = 25;
GROUP_CHANGE_ANNOUNCE = 26;
GROUP_PARTICIPANT_ADD = 27;
GROUP_PARTICIPANT_REMOVE = 28;
GROUP_PARTICIPANT_PROMOTE = 29;
GROUP_PARTICIPANT_DEMOTE = 30;
GROUP_PARTICIPANT_INVITE = 31;
GROUP_PARTICIPANT_LEAVE = 32;
GROUP_PARTICIPANT_CHANGE_NUMBER = 33;
BROADCAST_CREATE = 34;
BROADCAST_ADD = 35;
BROADCAST_REMOVE = 36;
GENERIC_NOTIFICATION = 37;
E2E_IDENTITY_CHANGED = 38;
E2E_ENCRYPTED = 39;
CALL_MISSED_VOICE = 40;
CALL_MISSED_VIDEO = 41;
INDIVIDUAL_CHANGE_NUMBER = 42;
GROUP_DELETE = 43;
GROUP_ANNOUNCE_MODE_MESSAGE_BOUNCE = 44;
CALL_MISSED_GROUP_VOICE = 45;
CALL_MISSED_GROUP_VIDEO = 46;
PAYMENT_CIPHERTEXT = 47;
PAYMENT_FUTUREPROOF = 48;
PAYMENT_TRANSACTION_STATUS_UPDATE_FAILED = 49;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUNDED = 50;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUND_FAILED = 51;
PAYMENT_TRANSACTION_STATUS_RECEIVER_PENDING_SETUP = 52;
PAYMENT_TRANSACTION_STATUS_RECEIVER_SUCCESS_AFTER_HICCUP = 53;
PAYMENT_ACTION_ACCOUNT_SETUP_REMINDER = 54;
PAYMENT_ACTION_SEND_PAYMENT_REMINDER = 55;
PAYMENT_ACTION_SEND_PAYMENT_INVITATION = 56;
PAYMENT_ACTION_REQUEST_DECLINED = 57;
PAYMENT_ACTION_REQUEST_EXPIRED = 58;
PAYMENT_ACTION_REQUEST_CANCELLED = 59;
BIZ_VERIFIED_TRANSITION_TOP_TO_BOTTOM = 60;
BIZ_VERIFIED_TRANSITION_BOTTOM_TO_TOP = 61;
BIZ_INTRO_TOP = 62;
BIZ_INTRO_BOTTOM = 63;
BIZ_NAME_CHANGE = 64;
BIZ_MOVE_TO_CONSUMER_APP = 65;
BIZ_TWO_TIER_MIGRATION_TOP = 66;
BIZ_TWO_TIER_MIGRATION_BOTTOM = 67;
OVERSIZED = 68;
GROUP_CHANGE_NO_FREQUENTLY_FORWARDED = 69;
GROUP_V4_ADD_INVITE_SENT = 70;
GROUP_PARTICIPANT_ADD_REQUEST_JOIN = 71;
CHANGE_EPHEMERAL_SETTING = 72;
}
optional WEB_MESSAGE_INFO_STUBTYPE messageStubType = 24;
optional bool clearMedia = 25;
repeated string messageStubParameters = 26;
optional uint32 duration = 27;
repeated string labels = 28;
optional PaymentInfo paymentInfo = 29;
optional LiveLocationMessage finalLiveLocation = 30;
optional PaymentInfo quotedPaymentInfo = 31;
optional uint64 ephemeralStartTimestamp = 32;
optional uint32 ephemeralDuration = 33;
}

183
vendor/github.com/Rhymen/go-whatsapp/chat_history.go generated vendored Normal file
View File

@@ -0,0 +1,183 @@
package whatsapp
import (
"github.com/Rhymen/go-whatsapp/binary"
"github.com/Rhymen/go-whatsapp/binary/proto"
"log"
"strconv"
"time"
)
type MessageOffsetInfo struct {
FirstMessageId string
FirstMessageOwner bool
}
func decodeMessages(n *binary.Node) []*proto.WebMessageInfo {
var messages = make([]*proto.WebMessageInfo, 0)
if n == nil || n.Attributes == nil || n.Content == nil {
return messages
}
for _, msg := range n.Content.([]interface{}) {
switch msg.(type) {
case *proto.WebMessageInfo:
messages = append(messages, msg.(*proto.WebMessageInfo))
default:
log.Println("decodeMessages: Non WebMessage encountered")
}
}
return messages
}
// LoadChatMessages is useful to "scroll" messages, loading by count at a time
// if handlers == nil the func will use default handlers
// if after == true LoadChatMessages will load messages after the specified messageId, otherwise it will return
// message before the messageId
func (wac *Conn) LoadChatMessages(jid string, count int, messageId string, owner bool, after bool, handlers ...Handler) error {
if count <= 0 {
return nil
}
if handlers == nil {
handlers = wac.handler
}
kind := "before"
if after {
kind = "after"
}
node, err := wac.query("message", jid, messageId, kind,
strconv.FormatBool(owner), "", count, 0)
if err != nil {
wac.handleWithCustomHandlers(err, handlers)
return err
}
for _, msg := range decodeMessages(node) {
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
wac.handleWithCustomHandlers(msg, handlers)
}
return nil
}
// LoadFullChatHistory loads full chat history for the given jid
// chunkSize = how many messages to load with one query; if handlers == nil the func will use default handlers;
// pauseBetweenQueries = how much time to sleep between queries
func (wac *Conn) LoadFullChatHistory(jid string, chunkSize int,
pauseBetweenQueries time.Duration, handlers ...Handler) {
if chunkSize <= 0 {
return
}
if handlers == nil {
handlers = wac.handler
}
beforeMsg := ""
beforeMsgIsOwner := true
for {
node, err := wac.query("message", jid, beforeMsg, "before",
strconv.FormatBool(beforeMsgIsOwner), "", chunkSize, 0)
if err != nil {
wac.handleWithCustomHandlers(err, handlers)
} else {
msgs := decodeMessages(node)
for _, msg := range msgs {
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
wac.handleWithCustomHandlers(msg, handlers)
}
if len(msgs) == 0 {
break
}
beforeMsg = *msgs[0].Key.Id
beforeMsgIsOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe
}
<-time.After(pauseBetweenQueries)
}
}
// LoadFullChatHistoryAfter loads all messages after the specified messageId
// useful to "catch up" with the message history after some specified message
func (wac *Conn) LoadFullChatHistoryAfter(jid string, messageId string, chunkSize int,
pauseBetweenQueries time.Duration, handlers ...Handler) {
if chunkSize <= 0 {
return
}
if handlers == nil {
handlers = wac.handler
}
msgOwner := true
prevNotFound := false
for {
node, err := wac.query("message", jid, messageId, "after",
strconv.FormatBool(msgOwner), "", chunkSize, 0)
if err != nil {
// Whatsapp will return 404 status when there is wrong owner flag on the requested message id
if err == ErrServerRespondedWith404 {
// this will detect two consecutive "not found" errors.
// this is done to prevent infinite loop when wrong message id supplied
if prevNotFound {
log.Println("LoadFullChatHistoryAfter: could not retrieve any messages, wrong message id?")
return
}
prevNotFound = true
// try to reverse the owner flag and retry
if msgOwner {
// reverse initial msgOwner value and retry
msgOwner = false
<-time.After(time.Second)
continue
}
}
// if the error isn't a 404 error, pass it to the error handler
wac.handleWithCustomHandlers(err, handlers)
} else {
msgs := decodeMessages(node)
for _, msg := range msgs {
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
wac.handleWithCustomHandlers(msg, handlers)
}
if len(msgs) != chunkSize {
break
}
messageId = *msgs[0].Key.Id
msgOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe
}
// message was found
prevNotFound = false
<-time.After(pauseBetweenQueries)
}
}

245
vendor/github.com/Rhymen/go-whatsapp/conn.go generated vendored Normal file
View File

@@ -0,0 +1,245 @@
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
package whatsapp
import (
"math/rand"
"net/http"
"net/url"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
)
type metric byte
const (
debugLog metric = iota + 1
queryResume
queryReceipt
queryMedia
queryChat
queryContacts
queryMessages
presence
presenceSubscribe
group
read
chat
received
pic
status
message
queryActions
block
queryGroup
queryPreview
queryEmoji
queryMessageInfo
spam
querySearch
queryIdentity
queryUrl
profile
contact
queryVcard
queryStatus
queryStatusUpdate
privacyStatus
queryLiveLocations
liveLocation
queryVname
queryLabels
call
queryCall
queryQuickReplies
)
type flag byte
const (
ignore flag = 1 << (7 - iota)
ackRequest
available
notAvailable
expires
skipOffline
)
/*
Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
It holds all necessary information to make the package work internally.
*/
type Conn struct {
ws *websocketWrapper
listener *listenerWrapper
connected bool
loggedIn bool
wg *sync.WaitGroup
session *Session
sessionLock uint32
handler []Handler
msgCount int
msgTimeout time.Duration
Info *Info
Store *Store
ServerLastSeen time.Time
longClientName string
shortClientName string
loginSessionLock sync.RWMutex
Proxy func(*http.Request) (*url.URL, error)
writerLock sync.RWMutex
}
type websocketWrapper struct {
sync.Mutex
conn *websocket.Conn
close chan struct{}
}
type listenerWrapper struct {
sync.RWMutex
m map[string]chan string
}
/*
Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
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",
}
return wac, wac.connect()
}
// 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) {
wac := &Conn{
handler: make([]Handler, 0),
msgCount: 0,
msgTimeout: timeout,
Store: newStore(),
longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp",
Proxy: proxy,
}
return wac, wac.connect()
}
// connect should be guarded with wsWriteMutex
func (wac *Conn) connect() (err error) {
if wac.connected {
return ErrAlreadyConnected
}
wac.connected = true
defer func() { // set connected to false on error
if err != nil {
wac.connected = false
}
}()
dialer := &websocket.Dialer{
ReadBufferSize: 25 * 1024 * 1024,
WriteBufferSize: 10 * 1024 * 1024,
HandshakeTimeout: wac.msgTimeout,
Proxy: wac.Proxy,
}
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
if err != nil {
return errors.Wrap(err, "couldn't dial whatsapp web websocket")
}
wsConn.SetCloseHandler(func(code int, text string) error {
// from default CloseHandler
message := websocket.FormatCloseMessage(code, "")
err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
// our close handling
_, _ = wac.Disconnect()
wac.handle(&ErrConnectionClosed{Code: code, Text: text})
return err
})
wac.ws = &websocketWrapper{
conn: wsConn,
close: make(chan struct{}),
}
wac.listener = &listenerWrapper{
m: make(map[string]chan string),
}
wac.wg = &sync.WaitGroup{}
wac.wg.Add(2)
go wac.readPump()
go wac.keepAlive(20000, 60000)
wac.loggedIn = false
return nil
}
func (wac *Conn) Disconnect() (Session, error) {
if !wac.connected {
return Session{}, ErrNotConnected
}
wac.connected = false
wac.loggedIn = false
close(wac.ws.close) //signal close
wac.wg.Wait() //wait for close
err := wac.ws.conn.Close()
wac.ws = nil
if wac.session == nil {
return Session{}, err
}
return *wac.session, err
}
func (wac *Conn) AdminTest() (bool, error) {
if !wac.connected {
return false, ErrNotConnected
}
if !wac.loggedIn {
return false, ErrInvalidSession
}
result, err := wac.sendAdminTest()
return result, err
}
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
defer wac.wg.Done()
for {
err := wac.sendKeepAlive()
if err != nil {
wac.handle(errors.Wrap(err, "keepAlive failed"))
//TODO: Consequences?
}
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
select {
case <-time.After(time.Duration(interval) * time.Millisecond):
case <-wac.ws.close:
return
}
}
}

View File

@@ -2,7 +2,7 @@ package whatsapp
import (
"fmt"
"github.com/matterbridge/go-whatsapp/binary"
"github.com/Rhymen/go-whatsapp/binary"
"strconv"
"time"
)
@@ -10,29 +10,29 @@ import (
type Presence string
const (
PresenceAvailable = "available"
PresenceUnavailable = "unavailable"
PresenceComposing = "composing"
PresenceRecording = "recording"
PresencePaused = "paused"
PresenceAvailable Presence = "available"
PresenceUnavailable Presence = "unavailable"
PresenceComposing Presence = "composing"
PresenceRecording Presence = "recording"
PresencePaused Presence = "paused"
)
//TODO: filename? WhatsApp uses Store.Contacts for these functions
//TODO: functions probably shouldn't return a string, maybe build a struct / return json
//TODO: check for further queries
// functions probably shouldn't return a string, maybe build a struct / return json
// check for further queries
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
data := []interface{}{"query", "ProfilePicThumb", jid}
return wac.write(data)
return wac.writeJson(data)
}
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
data := []interface{}{"query", "Status", jid}
return wac.write(data)
return wac.writeJson(data)
}
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
data := []interface{}{"action", "presence", "subscribe", jid}
return wac.write(data)
return wac.writeJson(data)
}
func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) {
@@ -51,6 +51,10 @@ func (wac *Conn) LoadMessagesAfter(jid, messageId string, count int) (*binary.No
return wac.query("message", jid, messageId, "after", "true", "", count, 0)
}
func (wac *Conn) LoadMediaInfo(jid, messageId, owner string) (*binary.Node, error) {
return wac.query("media", jid, messageId, "", owner, "", 0, 0)
}
func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error) {
ts := time.Now().Unix()
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
@@ -84,7 +88,7 @@ func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error)
func (wac *Conn) Exist(jid string) (<-chan string, error) {
data := []interface{}{"query", "exist", jid}
return wac.write(data)
return wac.writeJson(data)
}
func (wac *Conn) Emoji() (*binary.Node, error) {
@@ -163,7 +167,12 @@ func (wac *Conn) query(t, jid, messageId, kind, owner, search string, count, pag
n.Attributes["page"] = strconv.Itoa(page)
}
ch, err := wac.writeBinary(n, group, ignore, tag)
metric := group
if t == "media" {
metric = queryMedia
}
ch, err := wac.writeBinary(n, metric, ignore, tag)
if err != nil {
return nil, err
}

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