Compare commits

...

83 Commits

Author SHA1 Message Date
Wim
e3d8fe4fd8 Release v1.18.0 (#1176) 2020-07-24 21:00:57 +02:00
Wim
23d8742f0d Update dependencies for 1.18.0 release (#1175) 2020-07-18 17:27:41 +02:00
Wim
3b6a8be07b Update README.md 2020-07-18 17:26:19 +02:00
Gary Kim
71a5b72aff Add Nextcloud Talk support (#1167)
Signed-off-by: Gary Kim <gary@garykim.dev>
2020-07-18 16:08:25 +02:00
z3bra
213bf349c3 Add an option to log into a file rather than stdout (#1168)
Use Logfile option in the `[general]` section
2020-07-18 15:46:19 +02:00
Andrey Groshev
a94fe55886 Fix MarkdownV2 support in Telegram (#1169) 2020-07-12 22:40:22 +02:00
haykam821
9b22f16497 Add websocket to API (#970)
Co-authored-by: Qais Patankar <qaisjp@gmail.com>
2020-07-12 21:13:28 +02:00
Wim
2977a5957e Update README 2020-06-28 19:02:28 +02:00
Wim
f70d1c897a Set fetch-depth to 0 to fetch all tags 2020-06-28 18:30:18 +02:00
Wim
a4a3525265 Set fetch-depth correct and use vendor when building in workflow 2020-06-28 18:23:46 +02:00
Wim
a6dd8446e4 Increase fetch depth in workflow 2020-06-28 18:17:26 +02:00
Wim
7bf9e1cfb3 Fix space in workflow 2020-06-28 18:13:50 +02:00
Wim
f291832a77 Upload artifacts on commit 2020-06-28 18:11:02 +02:00
Nathanaël
1fee323247 Reload user information when a new contact is detected (whatsapp) (#1160)
Before returning an empty string, we refresh the WhatsApp contacts and if we found the one we wanted, we can return a real name. Fixes #796
2020-06-25 00:35:49 +02:00
Qais Patankar
a41accd033 Add sane RemoteNickFormat default for API (#1157) 2020-06-25 00:25:10 +02:00
James Lu
37f7caf7f3 Skip gIRC built-in rate limiting (irc) (#1164)
By default, gIRC rate limits all outgoing messages. 
Since matterbridge already implements message throttling, this is extra layer of throttling is not necessary.
2020-06-24 23:57:37 +02:00
TheHolyRoger
5847f7758c Only colour IRC nicks if there is one. (#1161) 2020-06-24 23:48:54 +02:00
Wim
bce736993e Remove travis as it isn't working anymore 2020-06-24 23:45:45 +02:00
Wim
5636992446 Increase fetch-depth in workflow 2020-06-24 23:37:02 +02:00
Wim
f996a2b7ae More linting fixes 2020-06-24 23:28:41 +02:00
Wim
587de96ab3 Update golangci-lint config 2020-06-24 23:21:15 +02:00
Wim
80eb1cd202 Fix duplicate name in workflow 2020-06-24 22:37:46 +02:00
Wim
bbf594c815 Use github workflows 2020-06-24 22:36:47 +02:00
Sandro
2f0f2ee40d Combine runs to one layer (#1151) 2020-05-28 00:31:32 +02:00
Wim
96022d3aaf Bump version 2020-05-24 22:44:10 +02:00
Wim
8eb5e3cbf8 Release v1.17.5 (#1150) 2020-05-24 22:35:56 +02:00
xnaas
ddc2625934 Update Dockerfile so inotify works (#1148)
This change would be required for the Docker image to actually read `RELOADABLE` config options from the `matterbridge.toml`.

This edit would require https://github.com/42wim/matterbridge/wiki/Deploy:-Docker to be updated as well to mention that mounting would have to change to mounting a ***directory*** not a file. inotify inside Docker cannot read directly mounted files, only directories, for whatever reason.

This will preserve setups that were configured to run the old way without breaking them and new configs can be setup "correctly" without issue.
2020-05-24 22:01:52 +02:00
Wim
7f7ca697a0 Ignore non-user messages (msteams). Fixes #1141 (#1149)
Ignore these messages for now, also add a extra
debug option for msteams so we can dump the whole
message.
2020-05-24 15:49:24 +02:00
Alexander
900375679b Prevent re-requesting avatar data (xmpp) (#1117)
Prevent asking the server again and again for a
user's avatar if the server does not respond to
our initial request.
2020-05-24 14:07:36 +02:00
Wim
9440b9e313 Increase debug logging with function,file and linenumber (#1147)
Show the function name,file and linenumber like this
[0000]  INFO main:         [setupLogger:matterbridge.go:100] Enabling debug logging.
[0000]  INFO main:         [main:matterbridge.go:46] Running version 1.17.5-dev

Only enable this for debug as this adds some overhead.
2020-05-24 13:58:15 +02:00
Wim
393f9e998b Update dependencies / vendor (#1146) 2020-05-24 00:06:21 +02:00
Wim
ba0bfe70a8 Add StripMarkdown option (irc). (#1145)
Enable `StripMarkdown` to strip markdown for irc.
2020-05-23 21:46:15 +02:00
Wim
3c4a3e3f75 Implement xep-0245 (xmpp). Closes #1137 (#1144) 2020-05-23 20:51:04 +02:00
Wim
274fb09ed4 Fix forward from hidden users (telegram). Closes #1131 (#1143)
Use ForwardDate to check if a message is forwarded.
If we have a nil ForwardedFrom then make this an unknown user.
2020-05-23 19:15:26 +02:00
Wim
d44598a900 Add an option to disable sending HTML to matrix. Fixes #1022 (#1135) 2020-05-14 00:37:41 +02:00
Wim
c9cfa59f54 Do not use webhooks when token is configured (slack) (fixes #1123) (#1134) 2020-05-14 00:27:34 +02:00
Tiago Epifânio
7062234331 Avoid creating invalid url when the user doesn't have an avatar (matrix) (#1130) 2020-05-11 00:21:56 +02:00
Qais Patankar
9754569525 Fix webhook EventUserAction messages being skipped (discord) (#1133)
Fixes #1132
2020-05-11 00:20:35 +02:00
Qais Patankar
52a071e34d Fix #1049: missing space before embeds (discord) (#1124) 2020-05-07 00:19:48 +02:00
Qais Patankar
2d8f749e36 Fix #1120: replaceAction "_" crash (discord) (#1121) 2020-04-25 14:22:22 +02:00
Wim
a18cb74f03 Bump version 2020-04-22 00:00:08 +02:00
Wim
6c442e239d Release v1.17.4 (#1112) 2020-04-21 23:53:51 +02:00
Wim
eaf92fca4d Add an ID cache (discord). Fixes #1106 (#1111)
When a webhook "edits" a message, it does this by deleting the message
and creating a new one with the new content.

On creation of this new message, we'll get another ID then already is
know by the gateway in its id cache. So we add it in our own cache and
replace it whenever we want to edit/delete it again.
2020-04-21 23:35:46 +02:00
Wim
06b7bad714 Lowercase account names. Fixes #1108 (#1110) 2020-04-21 20:42:11 +02:00
Wim
19eec2ed03 Update Rhymen/go-whatsapp. Fixes #1107 (#1109) 2020-04-21 19:55:47 +02:00
Wim
d99c54343a Remove panics and retry polling on failure (msteams). Fixes #1104 (#1105) 2020-04-21 19:29:24 +02:00
Wim
308a110000 Bump version 2020-04-19 17:23:17 +02:00
Wim
4f406b2ce6 Release v1.17.3 (#1103) 2020-04-19 17:13:58 +02:00
Wim
e564c555d7 Clip too long messages on 3000 length (slack). Fixes #1081 (#1102) 2020-04-19 17:00:11 +02:00
Wim
f7ec9af9e8 Add extra space before colon in attachments (irc). Fixes #1089 (#1101) 2020-04-19 16:45:53 +02:00
Wim
4d93a774ce Ignore non-critical errors (whatsapp). Fixes #1094 (#1100) 2020-04-19 13:45:35 +02:00
Wim
2595dd30bf Update matterbridge/go-xmpp. Fixes #1097 (#1099) 2020-04-19 01:06:44 +02:00
Wim
9190365289 Add JoinDelay option (irc). Fixes #1084 (#1098) 2020-04-19 00:46:35 +02:00
Wim
57794b3b9f Prevent image/message looping (slack). Fixes #1088 (#1096)
Also check for our matterbridge ID in Blocks set in SubMessages.
2020-04-18 22:30:49 +02:00
ldruschk
3c36f651be Fix the behavior of ShowTopicChange and SyncTopic (#1086)
Currently, the "topic_change" events are ignored if both, 
ShowTopicChange and SyncTopic are set, and forwarded otherwise. 

This pull requests changes the behavior such that the events are 
only forwarded if one of those two config options is set to true 
and ignored otherwise.
2020-04-18 22:05:27 +02:00
ldruschk
8e6ddadba2 Relay Joins/Topic changes in RocketChat bridge (#1085)
This pull request properly sets the events EventJoinLeave and EventTopicChange for messages from the RocketChat bridge and drops messages which are neither one of those events nor plain messages.
2020-04-18 22:00:35 +02:00
Wim
8a87a71927 Update matterbridge/go-xmpp to add PEP-0030 support (#1095) 2020-04-18 20:58:55 +02:00
Qais Patankar
0047e6f523 Sort README bridge and library links (#1093) 2020-04-18 18:12:16 +02:00
Alexander
7183095a28 Implement User Avatar spoofing of XMPP users (#1090)
* Implement User Avatar spoofing of XMPP users
2020-04-16 22:16:25 +02:00
Wim
13c90893c7 Update matterbridge/Rocket.Chat.Go.SDK (#1087) 2020-04-16 21:48:53 +02:00
Wim
976fbcd07f Bump version 2020-04-09 23:03:45 +02:00
Wim
d97b077e85 Release v1.17.2 (#1080) 2020-04-09 22:54:51 +02:00
Wim
8950575bfb Update Rhymen/go-whatsapp vendor and whatsapp version (#1078) 2020-04-09 22:30:08 +02:00
Jerry Heiselman
11fc4c286f Clarify terminology used in mapping group chat IDs to channels in config (#1079)
* Clarify embedded docs for channel specification

Should help with #1072
2020-04-08 23:52:38 +02:00
Wim
8d08e348a9 Reset start timestamp on reconnect (whatsapp). Fixes #1059 (#1064) 2020-03-31 23:26:53 +02:00
Wim
a18807f19e Update matterbridge/go-xmpp to add xmpp avatar support (#1070) 2020-03-29 17:35:40 +02:00
Wim
29f658fd3c Use DebugWriter after upstream changes (xmpp) 2020-03-29 15:03:24 +02:00
Wim
a30bb8fed0 Sync matterbridge/go-xmpp with upstream 2020-03-29 15:03:24 +02:00
Wim
092ca1cd67 Update vendor slack-go/slack (#1068) 2020-03-28 23:50:47 +01:00
Wim
0df2539641 Use upstream yaegashi/msgraph.go/msauth (msteams) (#1067) 2020-03-28 23:44:49 +01:00
Wim
0f2d8a599c Update vendor d5/tengo (#1066) 2020-03-28 23:41:35 +01:00
Wim
54b3143a1d Bump version 2020-03-28 00:29:41 +01:00
Wim
148f7d2a91 Release v1.17.1 (#1063) 2020-03-28 00:18:29 +01:00
Wim
1aa662f763 Update client version whatsapp. Fixes #1061 (#1062)
See https://github.com/Rhymen/go-whatsapp/issues/305
2020-03-28 00:18:03 +01:00
Alex Wigen
0b86b88de7 Remove build dependencies from final docker image (multistage build) (#1057)
This multistage build takes the resulting image size down from 346MB to
90MB.
2020-03-22 22:55:29 +01:00
Qais Patankar
98033b1ba7 Don't transmit typing events from ourselves (slack/discord) (#1056) 2020-03-22 18:39:11 +01:00
Qais Patankar
2b7eab629d Add support for build tags (#1054)
By default all bridges are available.

You can turn off certain bridges by providing
e.g. "nodiscord" as a build tag.

    go build -tags nomsteams,noapi
2020-03-22 18:34:14 +01:00
Wim
0e4973e15c Exclude gateway/bridgemap from linting (#1055) 2020-03-22 14:35:48 +01:00
Qais Patankar
af0acf0dae Strip extra info from emotes (discord) (#1052) 2020-03-22 14:16:31 +01:00
Qais Patankar
76e5fe5a87 Update vendor yaegashi/msgraph.go to v0.1.2 (2) 2020-03-22 00:02:48 +01:00
Qais Patankar
802c80f40c Update vendor yaegashi/msgraph.go to v0.1.2 (1) 2020-03-22 00:02:48 +01:00
Wim
a51c5bd905 Add more msteams docs (#1051) 2020-03-21 23:30:22 +01:00
Wim
8c68556f52 Bump version 2020-03-21 22:51:22 +01:00
4908 changed files with 233843 additions and 293014 deletions

57
.github/workflows/development.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Development
on: [push, pull_request]
jobs:
lint:
name: golangci-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 20
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v1
with:
version: v1.27
args: "-v --new-from-rev HEAD~1"
test-build-upload:
strategy:
matrix:
go-version: [1.13.x, 1.14.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Test
run: go test ./... -mod=vendor
- name: Build
run: |
mkdir -p output/{win,lin,arm,mac}
VERSION=$(git describe --tags)
GOOS=linux GOARCH=amd64 go build -mod=vendor -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/lin/matterbridge-$VERSION-linux-amd64
GOOS=windows GOARCH=amd64 go build -mod=vendor -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
GOOS=darwin GOARCH=amd64 go build -mod=vendor -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
- name: Upload linux 64-bit
if: startsWith(matrix.go-version,'1.14')
uses: actions/upload-artifact@v2
with:
name: matterbridge-linux-64bit
path: output/lin
- name: Upload windows 64-bit
if: startsWith(matrix.go-version,'1.14')
uses: actions/upload-artifact@v2
with:
name: matterbridge-windows-64bit
path: output/win
- name: Upload darwin 64-bit
if: startsWith(matrix.go-version,'1.14')
uses: actions/upload-artifact@v2
with:
name: matterbridge-darwin-64bit
path: output/mac

View File

@@ -23,7 +23,7 @@ run:
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs:
skip-dirs: gateway/bridgemap$
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
@@ -176,6 +176,10 @@ linters:
- prealloc
- wsl
- gomnd
- godox
- goerr113
- testpackage
- godot
# rules to deal with reported isues

View File

@@ -1,56 +0,0 @@
language: go
go_import_path: github.com/42wim/matterbridge
# We have everything vendored so this helps TravisCI not run `go get ...`.
install: true
git:
depth: 200
notifications:
email: false
branches:
only:
- master
- /.*/
jobs:
include:
- stage: lint
# Run linting in one Go environment only.
script: ./ci/lint.sh
go: 1.14.x
env:
- GO111MODULE=on
- GOLANGCI_VERSION="v1.23.7"
- stage: test
# Run tests in a combination of Go environments.
script: ./ci/test.sh
go: 1.13.x
env:
- GOFLAGS=-mod=vendor
- script: ./ci/test.sh
go: 1.13.x
env:
- GO111MODULE=on
- script: ./ci/test.sh
go: 1.14.x
env:
- GO111MODULE=on
- REPORT_COVERAGE=1
- BINDEPLOY=1
before_deploy: /bin/bash ci/bintray.sh
deploy:
on:
all_branches: true
condition: $BINDEPLOY = 1
provider: bintray
edge:
branch: v1.8.47
file: ci/deploy.json
user: 42wim
key:
secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI="

View File

@@ -1,11 +1,16 @@
FROM alpine:edge
ENTRYPOINT ["/bin/matterbridge"]
FROM alpine:edge AS builder
COPY . /go/src/github.com/42wim/matterbridge
RUN apk update && apk add go git gcc musl-dev ca-certificates mailcap \
RUN apk update && apk add go git gcc musl-dev \
&& cd /go/src/github.com/42wim/matterbridge \
&& export GOPATH=/go \
&& go get \
&& go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
&& rm -rf /go \
&& apk del --purge git go gcc musl-dev
&& go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
FROM alpine:edge
RUN apk --no-cache add ca-certificates mailcap
COPY --from=builder /bin/matterbridge /bin/matterbridge
RUN mkdir /etc/matterbridge \
&& touch /etc/matterbridge/matterbridge.toml \
&& ln -sf /matterbridge.toml /etc/matterbridge/matterbridge.toml
ENTRYPOINT ["/bin/matterbridge", "-conf", "/etc/matterbridge/matterbridge.toml"]

View File

@@ -9,26 +9,26 @@ Letting people be where they want to be.<br />
<sup>
[Discord][mb-discord] |
[Gitter][mb-gitter] |
[IRC][mb-irc] |
[Discord][mb-discord] |
[Keybase][mb-keybase] |
[Matrix][mb-matrix] |
[Slack][mb-slack] |
[Mattermost][mb-mattermost] |
[MSTeams][mb-msteams] |
[Rocket.Chat][mb-rocketchat] |
[XMPP][mb-xmpp] |
[Slack][mb-slack] |
[Telegram][mb-telegram] |
[Twitch][mb-twitch] |
[WhatsApp][mb-whatsapp] |
[XMPP][mb-xmpp] |
[Zulip][mb-zulip] |
[Telegram][mb-telegram] |
[Keybase][mb-keybase] |
And more...
</sup>
---
[![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 />
@@ -86,29 +86,32 @@ And more...
### Natively supported
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
- [IRC](http://www.mirc.com/servers.html)
- [XMPP](https://xmpp.org)
- [Gitter](https://gitter.im)
- [Slack](https://slack.com)
- [Discord](https://discordapp.com)
- [Telegram](https://telegram.org)
- [Rocket.chat](https://rocket.chat)
- [Matrix](https://matrix.org)
- [Steam](https://store.steampowered.com/)
- [Twitch](https://twitch.tv)
- [Ssh-chat](https://github.com/shazow/ssh-chat)
- [WhatsApp](https://www.whatsapp.com/)
- [Zulip](https://zulipchat.com)
- [Gitter](https://gitter.im)
- [IRC](http://www.mirc.com/servers.html)
- [Keybase](https://keybase.io)
- [Matrix](https://matrix.org)
- [Mattermost](https://github.com/mattermost/mattermost-server/) 4.x, 5.x
- [Microsoft Teams](https://teams.microsoft.com)
- [Nextcloud Talk](https://nextcloud.com/talk/)
- [Rocket.chat](https://rocket.chat)
- [Slack](https://slack.com)
- [Ssh-chat](https://github.com/shazow/ssh-chat)
- [Steam](https://store.steampowered.com/)
- [Telegram](https://telegram.org)
- [Twitch](https://twitch.tv)
- [WhatsApp](https://www.whatsapp.com/)
- [XMPP](https://xmpp.org)
- [Zulip](https://zulipchat.com)
### 3rd party via matterbridge api
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Minecraft](https://github.com/elytra/MatterLink)
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
- [Facebook messenger](https://github.com/VictorNine/fbridge)
- [Discourse](https://github.com/DeclanHoare/matterbabble)
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
### API
@@ -128,18 +131,18 @@ Used by the projects below. Feel free to make a PR to add your project to this l
Questions or want to test on your favorite platform? Join below:
- [Discord][mb-discord]
- [Gitter][mb-gitter]
- [IRC][mb-irc]
- [Discord][mb-discord]
- [Keybase][mb-keybase]
- [Matrix][mb-matrix]
- [Slack][mb-slack]
- [Mattermost][mb-mattermost]
- [Rocket.Chat][mb-rocketchat]
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
- [Twitch][mb-twitch]
- [Zulip][mb-zulip]
- [Slack][mb-slack]
- [Telegram][mb-telegram]
- [Keybase][mb-keybase]
- [Twitch][mb-twitch]
- [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
- [Zulip][mb-zulip]
## Screenshots
@@ -149,14 +152,15 @@ See https://github.com/42wim/matterbridge/wiki
### Binaries
- Latest stable release [v1.17.0](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
- Latest stable release [v1.18.0](https://github.com/42wim/matterbridge/releases/latest)
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
### Packages
- [Overview](https://repology.org/metapackage/matterbridge/versions)
- [snap](https://snapcraft.io/matterbridge)
## Building
@@ -304,6 +308,7 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
- https://kopano.com/blog/matterbridge-bridging-mattermost-chat/
- https://www.stitcher.com/s/?eid=52382713
- https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
- https://userlinux.net/mattermost-and-matterbridge.html
## Thanks
@@ -322,30 +327,32 @@ Matterbridge wouldn't exist without these libraries:
- gops - https://github.com/google/gops
- gozulipbot - https://github.com/ifo/gozulipbot
- irc - https://github.com/lrstanley/girc
- mattermost - https://github.com/mattermost/mattermost-server
- keybase - https://github.com/keybase/go-keybase-chat-bot
- matrix - https://github.com/matrix-org/gomatrix
- sshchat - https://github.com/shazow/ssh-chat
- mattermost - https://github.com/mattermost/mattermost-server
- msgraph.go - https://github.com/yaegashi/msgraph.go
- slack - https://github.com/nlopes/slack
- sshchat - https://github.com/shazow/ssh-chat
- steam - https://github.com/Philipp15b/go-steam
- telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
- xmpp - https://github.com/mattn/go-xmpp
- whatsapp - https://github.com/Rhymen/go-whatsapp/
- zulip - https://github.com/ifo/gozulipbot
- tengo - https://github.com/d5/tengo
- keybase - https://github.com/keybase/go-keybase-chat-bot
- whatsapp - https://github.com/Rhymen/go-whatsapp/
- xmpp - https://github.com/mattn/go-xmpp
- zulip - https://github.com/ifo/gozulipbot
<!-- Links -->
[mb-discord]: https://discord.gg/AkKPtrQ
[mb-gitter]: https://gitter.im/42wim/matterbridge
[mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat
[mb-discord]: https://discord.gg/AkKPtrQ
[mb-keybase]: https://keybase.io/team/matterbridge
[mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
[mb-msteams]: https://teams.microsoft.com/join/hj92x75gd3y7
[mb-rocketchat]: https://open.rocket.chat/channel/matterbridge
[mb-xmpp]: https://inverse.chat/
[mb-slack]: https://join.slack.com/matterbridgechat/shared_invite/MjEwODMxNjU1NDMwLTE0OTk2MTU3NTMtMzZkZmRiNDZhOA
[mb-telegram]: https://t.me/Matterbridge
[mb-twitch]: https://www.twitch.tv/matterbridge
[mb-whatsapp]: https://www.whatsapp.com/
[mb-keybase]: https://keybase.io/team/matterbridge
[mb-xmpp]: https://inverse.chat/
[mb-zulip]: https://matterbridge.zulipchat.com/register/
[mb-telegram]: https://t.me/Matterbridge

View File

@@ -8,9 +8,10 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/gorilla/websocket"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/zfjagann/golang-ring"
ring "github.com/zfjagann/golang-ring"
)
type API struct {
@@ -41,9 +42,17 @@ func New(cfg *bridge.Config) bridge.Bridger {
return key == b.GetString("Token"), nil
}))
}
// Set RemoteNickFormat to a sane default
if !b.IsKeySet("RemoteNickFormat") {
b.Log.Debugln("RemoteNickFormat is unset, defaulting to \"{NICK}\"")
b.Config.Config.Viper().Set(b.GetConfigKey("RemoteNickFormat"), "{NICK}")
}
e.GET("/api/health", b.handleHealthcheck)
e.GET("/api/messages", b.handleMessages)
e.GET("/api/stream", b.handleStream)
e.GET("/api/websocket", b.handleWebsocket)
e.POST("/api/message", b.handlePostMessage)
go func() {
if b.GetString("BindAddress") == "" {
@@ -106,13 +115,17 @@ func (b *API) handleMessages(c echo.Context) error {
return nil
}
func (b *API) handleStream(c echo.Context) error {
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
c.Response().WriteHeader(http.StatusOK)
greet := config.Message{
func (b *API) getGreeting() config.Message {
return config.Message{
Event: config.EventAPIConnected,
Timestamp: time.Now(),
}
}
func (b *API) handleStream(c echo.Context) error {
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
c.Response().WriteHeader(http.StatusOK)
greet := b.getGreeting()
if err := json.NewEncoder(c.Response()).Encode(greet); err != nil {
return err
}
@@ -128,3 +141,52 @@ func (b *API) handleStream(c echo.Context) error {
time.Sleep(200 * time.Millisecond)
}
}
func (b *API) handleWebsocketMessage(message config.Message) {
message.Channel = "api"
message.Protocol = "api"
message.Account = b.Account
message.ID = ""
message.Timestamp = time.Now()
b.Log.Debugf("Sending websocket message from %s on %s to gateway", message.Username, "api")
b.Remote <- message
}
func (b *API) writePump(conn *websocket.Conn) {
for {
msg := b.Messages.Dequeue()
if msg != nil {
err := conn.WriteJSON(msg)
if err != nil {
break
}
}
}
}
func (b *API) readPump(conn *websocket.Conn) {
for {
message := config.Message{}
err := conn.ReadJSON(&message)
if err != nil {
break
}
b.handleWebsocketMessage(message)
}
}
func (b *API) handleWebsocket(c echo.Context) error {
conn, err := websocket.Upgrade(c.Response().Writer, c.Request(), nil, 1024, 1024)
if err != nil {
return err
}
greet := b.getGreeting()
_ = conn.WriteJSON(greet)
go b.writePump(conn)
go b.readPump(conn)
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"log"
"strings"
"sync"
"time"
"github.com/42wim/matterbridge/bridge/config"
"github.com/sirupsen/logrus"
@@ -74,6 +75,7 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
for ID, channel := range channels {
if !exists[ID] {
b.Log.Infof("%s: joining %s (ID: %s)", b.Account, channel.Name, ID)
time.Sleep(time.Duration(b.GetInt("JoinDelay")) * time.Millisecond)
err := b.JoinChannel(channel)
if err != nil {
return err
@@ -84,8 +86,16 @@ func (b *Bridge) joinChannels(channels map[string]config.ChannelInfo, exists map
return nil
}
func (b *Bridge) GetConfigKey(key string) string {
return b.Account + "." + key
}
func (b *Bridge) IsKeySet(key string) bool {
return b.Config.IsKeySet(b.GetConfigKey(key)) || b.Config.IsKeySet("general."+key)
}
func (b *Bridge) GetBool(key string) bool {
val, ok := b.Config.GetBool(b.Account + "." + key)
val, ok := b.Config.GetBool(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetBool("general." + key)
}
@@ -93,7 +103,7 @@ func (b *Bridge) GetBool(key string) bool {
}
func (b *Bridge) GetInt(key string) int {
val, ok := b.Config.GetInt(b.Account + "." + key)
val, ok := b.Config.GetInt(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetInt("general." + key)
}
@@ -101,7 +111,7 @@ func (b *Bridge) GetInt(key string) int {
}
func (b *Bridge) GetString(key string) string {
val, ok := b.Config.GetString(b.Account + "." + key)
val, ok := b.Config.GetString(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetString("general." + key)
}
@@ -109,7 +119,7 @@ func (b *Bridge) GetString(key string) string {
}
func (b *Bridge) GetStringSlice(key string) []string {
val, ok := b.Config.GetStringSlice(b.Account + "." + key)
val, ok := b.Config.GetStringSlice(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetStringSlice("general." + key)
}
@@ -117,7 +127,7 @@ func (b *Bridge) GetStringSlice(key string) []string {
}
func (b *Bridge) GetStringSlice2D(key string) [][]string {
val, ok := b.Config.GetStringSlice2D(b.Account + "." + key)
val, ok := b.Config.GetStringSlice2D(b.GetConfigKey(key))
if !ok {
val, _ = b.Config.GetStringSlice2D("general." + key)
}

View File

@@ -3,6 +3,7 @@ package config
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
@@ -84,13 +85,16 @@ type Protocol struct {
DisableWebPagePreview bool // telegram
EditSuffix string // mattermost, slack, discord, telegram, gitter
EditDisable bool // mattermost, slack, discord, telegram, gitter
HTMLDisable bool // matrix
IconURL string // mattermost, slack
IgnoreFailureOnStart bool // general
IgnoreNicks string // all protocols
IgnoreMessages string // all protocols
Jid string // xmpp
JoinDelay string // all protocols
Label string // all protocols
Login string // mattermost, matrix
LogFile string // general
MediaDownloadBlackList []string
MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
MediaDownloadSize int // all protocols
@@ -134,6 +138,7 @@ type Protocol struct {
SkipTLSVerify bool // IRC, mattermost
SkipVersionCheck bool // mattermost
StripNick bool // all protocols
StripMarkdown bool // irc
SyncTopic bool // slack
TengoModifyMessage string // general
Team string // mattermost, keybase
@@ -216,6 +221,7 @@ type BridgeValues struct {
type Config interface {
Viper() *viper.Viper
BridgeValues() *BridgeValues
IsKeySet(key string) bool
GetBool(key string) (bool, bool)
GetInt(key string) (int, bool)
GetString(key string) (string, bool)
@@ -243,6 +249,15 @@ func NewConfig(rootLogger *logrus.Logger, cfgfile string) Config {
cfgtype := detectConfigType(cfgfile)
mycfg := newConfigFromString(logger, input, cfgtype)
if mycfg.cv.General.LogFile != "" {
logfile, err := os.OpenFile(mycfg.cv.General.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err == nil {
logger.Info("Opening log file ", mycfg.cv.General.LogFile)
rootLogger.Out = logfile
} else {
logger.Warn("Failed to open ", mycfg.cv.General.LogFile)
}
}
if mycfg.cv.General.MediaDownloadSize == 0 {
mycfg.cv.General.MediaDownloadSize = 1000000
}
@@ -300,6 +315,12 @@ func (c *config) Viper() *viper.Viper {
return c.v
}
func (c *config) IsKeySet(key string) bool {
c.RLock()
defer c.RUnlock()
return c.v.IsSet(key)
}
func (c *config) GetBool(key string) (bool, bool) {
c.RLock()
defer c.RUnlock()
@@ -359,6 +380,11 @@ type TestConfig struct {
Overrides map[string]interface{}
}
func (c *TestConfig) IsKeySet(key string) bool {
_, ok := c.Overrides[key]
return ok || c.Config.IsKeySet(key)
}
func (c *TestConfig) GetBool(key string) (bool, bool) {
val, ok := c.Overrides[key]
if ok {

View File

@@ -21,6 +21,7 @@ type Bdiscord struct {
c *discordgo.Session
nick string
userID string
guildID string
webhookID string
webhookToken string
@@ -33,6 +34,8 @@ type Bdiscord struct {
membersMutex sync.RWMutex
userMemberMap map[string]*discordgo.Member
nickMemberMap map[string]*discordgo.Member
webhookCache map[string]string
webhookMutex sync.RWMutex
}
func New(cfg *bridge.Config) bridge.Bridger {
@@ -40,6 +43,7 @@ func New(cfg *bridge.Config) bridge.Bridger {
b.userMemberMap = make(map[string]*discordgo.Member)
b.nickMemberMap = make(map[string]*discordgo.Member)
b.channelInfoMap = make(map[string]*config.ChannelInfo)
b.webhookCache = make(map[string]string)
if b.GetString("WebhookURL") != "" {
b.Log.Debug("Configuring Discord Incoming Webhook")
b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
@@ -92,6 +96,7 @@ func (b *Bdiscord) Connect() error {
}
serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1)
b.nick = userinfo.Username
b.userID = userinfo.ID
b.channelsMutex.Lock()
for _, guild := range guilds {
if guild.Name == serverName || guild.ID == serverName {
@@ -186,6 +191,8 @@ func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
func (b *Bdiscord) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
origMsgID := msg.ID
channelID := b.getChannelID(msg.Channel)
if channelID == "" {
return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
@@ -222,12 +229,13 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
// Use webhook to send the message
if wID != "" && msg.Event != config.EventMsgDelete {
// skip events
if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
return "", nil
}
// If we are editing a message, delete the old message
if msg.ID != "" {
msg.ID = b.getCacheID(msg.ID)
b.Log.Debugf("Deleting edited webhook message")
err := b.c.ChannelMessageDelete(channelID, msg.ID)
if err != nil {
@@ -271,6 +279,8 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
if msg == nil {
return "", nil
}
b.updateCacheID(origMsgID, msg.ID)
return msg.ID, nil
}
@@ -281,6 +291,7 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
if msg.ID == "" {
return "", nil
}
msg.ID = b.getCacheID(msg.ID)
err := b.c.ChannelMessageDelete(channelID, msg.ID)
return "", err
}

View File

@@ -36,6 +36,11 @@ func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart)
return
}
// Ignore our own typing messages
if m.UserID == b.userID {
return
}
rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
rmsg.Channel = b.getChannelName(m.ChannelID)
b.Remote <- rmsg
@@ -119,6 +124,9 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
rmsg.Event = config.EventUserAction
}
// Replace emotes
rmsg.Text = replaceEmotes(rmsg.Text)
b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account)
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
@@ -209,7 +217,7 @@ func handleEmbed(embed *discordgo.MessageEmbed) string {
i++
if i == 1 {
result += "embed: " + e
result += " embed: " + e
continue
}

View File

@@ -20,14 +20,14 @@ func TestHandleEmbed(t *testing.T) {
embed: &discordgo.MessageEmbed{
Title: "blah",
},
result: "embed: blah\n",
result: " embed: blah\n",
},
"two": {
embed: &discordgo.MessageEmbed{
Title: "blah",
Description: "blah2",
},
result: "embed: blah - blah2\n",
result: " embed: blah - blah2\n",
},
"three": {
embed: &discordgo.MessageEmbed{
@@ -35,20 +35,20 @@ func TestHandleEmbed(t *testing.T) {
Description: "blah2",
URL: "blah3",
},
result: "embed: blah - blah2 - blah3\n",
result: " embed: blah - blah2 - blah3\n",
},
"twob": {
embed: &discordgo.MessageEmbed{
Description: "blah2",
URL: "blah3",
},
result: "embed: blah2 - blah3\n",
result: " embed: blah2 - blah3\n",
},
"oneb": {
embed: &discordgo.MessageEmbed{
URL: "blah3",
},
result: "embed: blah3\n",
result: " embed: blah3\n",
},
}

View File

@@ -137,6 +137,7 @@ var (
// See https://discordapp.com/developers/docs/reference#message-formatting.
channelMentionRE = regexp.MustCompile("<#[0-9]+>")
userMentionRE = regexp.MustCompile("@[^@\n]{1,32}")
emoteRE = regexp.MustCompile(`<a?(:\w+:)\d+>`)
)
func (b *Bdiscord) replaceChannelMentions(text string) string {
@@ -182,9 +183,14 @@ func (b *Bdiscord) replaceUserMentions(text string) string {
return userMentionRE.ReplaceAllStringFunc(text, replaceUserMentionFunc)
}
func replaceEmotes(text string) string {
return emoteRE.ReplaceAllString(text, "$1")
}
func (b *Bdiscord) replaceAction(text string) (string, bool) {
if strings.HasPrefix(text, "_") && strings.HasSuffix(text, "_") {
return text[1 : len(text)-1], true
length := len(text)
if length > 1 && text[0] == '_' && text[length-1] == '_' {
return text[1 : length-1], true
}
return text, false
}
@@ -203,6 +209,40 @@ func (b *Bdiscord) splitURL(url string) (string, string) {
return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken]
}
// getcacheID tries to find a corresponding msgID in the webhook cache.
// if not found returns the original request.
func (b *Bdiscord) getCacheID(msgID string) string {
b.webhookMutex.RLock()
defer b.webhookMutex.RUnlock()
for k, v := range b.webhookCache {
if msgID == k {
return v
}
}
return msgID
}
// updateCacheID updates the cache so that the newID takes the place of
// the original ID. This is used for edit/deletes in combination with webhooks
// as editing a message via webhook means deleting the message and creating a
// new message (with a new ID). This ID needs to be set instead of the original ID
func (b *Bdiscord) updateCacheID(origID, newID string) {
b.webhookMutex.Lock()
match := false
for k, v := range b.webhookCache {
if v == origID {
delete(b.webhookCache, k)
b.webhookCache[origID] = newID
match = true
continue
}
}
if !match && origID != "" {
b.webhookCache[origID] = newID
}
b.webhookMutex.Unlock()
}
func enumerateUsernames(s string) []string {
onlySpace := true
for _, r := range s {

View File

@@ -10,8 +10,8 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/dfordsoft/golib/ic"
"github.com/lrstanley/girc"
"github.com/missdeer/golib/ic"
"github.com/paulrosania/go-charset/charset"
"github.com/saintfish/chardet"
@@ -54,12 +54,12 @@ func (b *Birc) handleFiles(msg *config.Message) bool {
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.Comment != "" {
msg.Text += fi.Comment + ": "
msg.Text += fi.Comment + " : "
}
if fi.URL != "" {
msg.Text = fi.URL
if fi.Comment != "" {
msg.Text = fi.Comment + ": " + fi.URL
msg.Text = fi.Comment + " : " + fi.URL
}
}
b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}

View File

@@ -14,6 +14,7 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/lrstanley/girc"
stripmd "github.com/writeas/go-strip-markdown"
// We need to import the 'data' package as an implicit dependency.
// See: https://godoc.org/github.com/paulrosania/go-charset/charset
@@ -156,6 +157,10 @@ func (b *Birc) Send(msg config.Message) (string, error) {
}
var msgLines []string
if b.GetBool("StripMarkdown") {
msg.Text = stripmd.Strip(msg.Text)
}
if b.GetBool("MessageSplit") {
msgLines = helper.GetSubLines(msg.Text, b.MessageLength)
} else {
@@ -201,7 +206,7 @@ func (b *Birc) doSend() {
for msg := range b.Local {
<-throttle.C
username := msg.Username
if b.GetBool("Colornicks") {
if b.GetBool("Colornicks") && len(username) > 1 {
checksum := crc32.ChecksumIEEE([]byte(msg.Username))
colorCode := checksum%14 + 2 // quick fix - prevent white or black color codes
username = fmt.Sprintf("\x03%02d%s\x0F", colorCode, msg.Username)
@@ -245,6 +250,8 @@ func (b *Birc) getClient() (*girc.Client, error) {
SSL: b.GetBool("UseTLS"),
TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
PingDelay: time.Minute,
// skip gIRC internal rate limiting, since we have our own throttling
AllowFlood: true,
})
return i, nil
}

View File

@@ -124,6 +124,14 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
return resp.EventID, err
}
if b.GetBool("HTMLDisable") {
resp, err := b.mc.SendText(channel, msg.Username+msg.Text)
if err != nil {
return "", err
}
return resp.EventID, err
}
username := html.EscapeString(msg.Username)
// check if we have a </tag>. if we have, we don't escape HTML. #696
if b.htmlTag.MatchString(msg.Username) {
@@ -372,6 +380,8 @@ func (b *Bmatrix) getAvatarURL(sender string) string {
return ""
}
url := strings.ReplaceAll(mxcURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/")
url += "?width=37&height=37&method=crop"
if url != "" {
url += "?width=37&height=37&method=crop"
}
return url
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
msgraph "github.com/matterbridge/msgraph.go/beta"
msgraph "github.com/yaegashi/msgraph.go/beta"
)
func (b *Bmsteams) findFile(weburl string) (string, error) {

View File

@@ -10,11 +10,11 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/davecgh/go-spew/spew"
// "github.com/davecgh/go-spew/spew"
msgraph "github.com/matterbridge/msgraph.go/beta"
"github.com/matterbridge/msgraph.go/msauth"
"github.com/mattn/godown"
msgraph "github.com/yaegashi/msgraph.go/beta"
"github.com/yaegashi/msgraph.go/msauth"
"golang.org/x/oauth2"
)
@@ -72,7 +72,15 @@ func (b *Bmsteams) Disconnect() error {
}
func (b *Bmsteams) JoinChannel(channel config.ChannelInfo) error {
go b.poll(channel.Name)
go func(name string) {
for {
err := b.poll(name)
if err != nil {
b.Log.Errorf("polling failed for %s: %s. retrying in 5 seconds", name, err)
}
time.Sleep(time.Second * 5)
}
}(channel.Name)
return nil
}
@@ -121,12 +129,12 @@ func (b *Bmsteams) getMessages(channel string) ([]msgraph.ChatMessage, error) {
}
//nolint:gocognit
func (b *Bmsteams) poll(channelName string) {
func (b *Bmsteams) poll(channelName string) error {
msgmap := make(map[string]time.Time)
b.Log.Debug("getting initial messages")
res, err := b.getMessages(channelName)
if err != nil {
panic(err)
return err
}
for _, msg := range res {
msgmap[*msg.ID] = *msg.CreatedDateTime
@@ -139,7 +147,7 @@ func (b *Bmsteams) poll(channelName string) {
for {
res, err := b.getMessages(channelName)
if err != nil {
panic(err)
return err
}
for i := len(res) - 1; i >= 0; i-- {
msg := res[i]
@@ -151,11 +159,22 @@ func (b *Bmsteams) poll(channelName string) {
continue
}
}
if b.GetBool("debug") {
b.Log.Debug("Msg dump: ", spew.Sdump(msg))
}
// skip non-user message for now.
if msg.From.User == nil {
continue
}
if *msg.From.User.ID == b.botID {
b.Log.Debug("skipping own message")
msgmap[*msg.ID] = *msg.CreatedDateTime
continue
}
msgmap[*msg.ID] = *msg.CreatedDateTime
if msg.LastModifiedDateTime != nil {
msgmap[*msg.ID] = *msg.LastModifiedDateTime

114
bridge/nctalk/nctalk.go Normal file
View File

@@ -0,0 +1,114 @@
package nctalk
import (
"context"
"strconv"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
talk "gomod.garykim.dev/nc-talk"
"gomod.garykim.dev/nc-talk/ocs"
"gomod.garykim.dev/nc-talk/room"
"gomod.garykim.dev/nc-talk/user"
)
type Btalk struct {
user *user.TalkUser
rooms []Broom
*bridge.Config
}
func New(cfg *bridge.Config) bridge.Bridger {
return &Btalk{Config: cfg}
}
type Broom struct {
room *room.TalkRoom
ctx context.Context
ctxCancel context.CancelFunc
}
func (b *Btalk) Connect() error {
b.Log.Info("Connecting")
b.user = talk.NewUser(b.GetString("Server"), b.GetString("Login"), b.GetString("Password"))
_, err := b.user.Capabilities()
if err != nil {
b.Log.Error("Cannot Connect")
return err
}
b.Log.Info("Connected")
return nil
}
func (b *Btalk) Disconnect() error {
for _, r := range b.rooms {
r.ctxCancel()
}
return nil
}
func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
newRoom := Broom{
room: talk.NewRoom(b.user, channel.Name),
}
newRoom.ctx, newRoom.ctxCancel = context.WithCancel(context.Background())
c, err := newRoom.room.ReceiveMessages(newRoom.ctx)
if err != nil {
return err
}
b.rooms = append(b.rooms, newRoom)
go func() {
for msg := range c {
// ignore messages that are one of the following
// * not a message from a user
// * from ourselves
if msg.MessageType != ocs.MessageComment || msg.ActorID == b.user.User {
continue
}
remoteMessage := config.Message{
Text: msg.Message,
Channel: newRoom.room.Token,
Username: msg.ActorDisplayName,
UserID: msg.ActorID,
Account: b.Account,
}
// It is possible for the ID to not be set on older versions of Talk so we only set it if
// the ID is not blank
if msg.ID != 0 {
remoteMessage.ID = strconv.Itoa(msg.ID)
}
b.Log.Debugf("<= Message is %#v", remoteMessage)
b.Remote <- remoteMessage
}
}()
return nil
}
func (b *Btalk) Send(msg config.Message) (string, error) {
r := b.getRoom(msg.Channel)
if r == nil {
b.Log.Errorf("Could not find room for %v", msg.Channel)
return "", nil
}
// Talk currently only supports sending normal messages
if msg.Event != "" {
return "", nil
}
sentMessage, err := r.room.SendMessage(msg.Username + msg.Text)
if err != nil {
b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err)
return "", nil
}
return strconv.Itoa(sentMessage.ID), nil
}
func (b *Btalk) getRoom(token string) *Broom {
for _, r := range b.rooms {
if r.room.Token == token {
return &r
}
}
return nil
}

View File

@@ -2,6 +2,7 @@ package brocketchat
import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/matterbridge/Rocket.Chat.Go.SDK/models"
)
func (b *Brocketchat) handleRocket() {
@@ -38,6 +39,23 @@ func (b *Brocketchat) handleRocketHook(messages chan *config.Message) {
}
}
func (b *Brocketchat) handleStatusEvent(ev models.Message, rmsg *config.Message) bool {
switch ev.Type {
case "":
// this is a normal message, no processing needed
// return true so the message is not dropped
return true
case sUserJoined, sUserLeft:
rmsg.Event = config.EventJoinLeave
return true
case sRoomChangedTopic:
rmsg.Event = config.EventTopicChange
return true
}
b.Log.Debugf("Dropping message with unknown type: %s", ev.Type)
return false
}
func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
for message := range b.messageChan {
// skip messages with same ID, apparently messages get duplicated for an unknown reason
@@ -59,7 +77,12 @@ func (b *Brocketchat) handleRocketClient(messages chan *config.Message) {
UserID: message.User.ID,
ID: message.ID,
}
messages <- rmsg
// handleStatusEvent returns false if the message should be dropped
// in that case it is probably some modification to the channel we do not want to relay
if b.handleStatusEvent(m, rmsg) {
messages <- rmsg
}
}
}

View File

@@ -29,6 +29,12 @@ type Brocketchat struct {
sync.RWMutex
}
const (
sUserJoined = "uj"
sUserLeft = "ul"
sRoomChangedTopic = "room_changed_topic"
)
func New(cfg *bridge.Config) bridge.Bridger {
newCache, err := lru.New(100)
if err != nil {

View File

@@ -1,6 +1,7 @@
package bslack
import (
"errors"
"fmt"
"html"
"time"
@@ -10,9 +11,12 @@ import (
"github.com/slack-go/slack"
)
// ErrEventIgnored is for events that should be ignored
var ErrEventIgnored = errors.New("this event message should ignored")
func (b *Bslack) handleSlack() {
messages := make(chan *config.Message)
if b.GetString(incomingWebhookConfig) != "" {
if b.GetString(incomingWebhookConfig) != "" && b.GetString(tokenConfig) == "" {
b.Log.Debugf("Choosing webhooks based receiving")
go b.handleMatterHook(messages)
} else {
@@ -53,7 +57,9 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
continue
}
rmsg, err := b.handleTypingEvent(ev)
if err != nil {
if err == ErrEventIgnored {
continue
} else if err != nil {
b.Log.Errorf("%#v", err)
continue
}
@@ -131,12 +137,6 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
}
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
if ev.Username == sSlackBotUser ||
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
return true
}
if ev.SubMessage != nil {
// It seems ev.SubMessage.Edited == nil when slack unfurls.
// Do not forward these messages. See Github issue #266.
@@ -149,6 +149,16 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
if ev.SubType == "message_replied" && ev.Hidden {
return true
}
if len(ev.SubMessage.Blocks.BlockSet) == 1 {
block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock)
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
}
}
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
if ev.Username == sSlackBotUser ||
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
return true
}
if len(ev.Files) > 0 {
@@ -276,6 +286,9 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
}
func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
if ev.User == b.si.User.ID {
return nil, ErrEventIgnored
}
channelInfo, err := b.channels.getChannelByID(ev.Channel)
if err != nil {
return nil, err

View File

@@ -64,6 +64,7 @@ const (
editSuffixConfig = "EditSuffix"
iconURLConfig = "iconurl"
noSendJoinConfig = "nosendjoinpart"
messageLength = 3000
)
func New(cfg *bridge.Config) bridge.Bridger {
@@ -194,6 +195,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg)
}
msg.Text = helper.ClipMessage(msg.Text, messageLength)
msg.Text = b.replaceCodeFence(msg.Text)
// Make a action /me of the message
@@ -202,7 +204,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
}
// Use webhook to send the message
if b.GetString(outgoingWebhookConfig) != "" {
if b.GetString(outgoingWebhookConfig) != "" && b.GetString(tokenConfig) == "" {
return "", b.sendWebhook(msg)
}
return b.sendRTM(msg)

View File

@@ -39,22 +39,32 @@ func (b *Btelegram) handleGroups(rmsg *config.Message, message *tgbotapi.Message
// handleForwarded handles forwarded messages
func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Message) {
if message.ForwardFrom != nil {
usernameForward := ""
if b.GetBool("UseFirstName") {
if message.ForwardDate == 0 {
return
}
if message.ForwardFrom == nil {
rmsg.Text = "Forwarded from " + unknownUser + ": " + rmsg.Text
return
}
usernameForward := ""
if b.GetBool("UseFirstName") {
usernameForward = message.ForwardFrom.FirstName
}
if usernameForward == "" {
usernameForward = message.ForwardFrom.UserName
if usernameForward == "" {
usernameForward = message.ForwardFrom.FirstName
}
if usernameForward == "" {
usernameForward = message.ForwardFrom.UserName
if usernameForward == "" {
usernameForward = message.ForwardFrom.FirstName
}
}
if usernameForward == "" {
usernameForward = unknownUser
}
rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
}
if usernameForward == "" {
usernameForward = unknownUser
}
rmsg.Text = "Forwarded from " + usernameForward + ": " + rmsg.Text
}
// handleQuoting handles quoting of previous messages
@@ -312,6 +322,9 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error
case "Markdown":
b.Log.Debug("Using mode markdown")
m.ParseMode = tgbotapi.ModeMarkdown
case MarkdownV2:
b.Log.Debug("Using mode MarkdownV2")
m.ParseMode = MarkdownV2
}
if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
b.Log.Debug("Using mode HTML - nick only")

View File

@@ -15,6 +15,7 @@ const (
unknownUser = "unknown"
HTMLFormat = "HTML"
HTMLNick = "htmlnick"
MarkdownV2 = "MarkdownV2"
)
type Btelegram struct {
@@ -126,6 +127,10 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string) (string, er
b.Log.Debug("Using mode markdown")
m.ParseMode = tgbotapi.ModeMarkdown
}
if b.GetString("MessageFormat") == MarkdownV2 {
b.Log.Debug("Using mode MarkdownV2")
m.ParseMode = MarkdownV2
}
if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
b.Log.Debug("Using mode HTML - nick only")
m.Text = username + html.EscapeString(text)

View File

@@ -23,7 +23,8 @@ Check:
// HandleError received from WhatsApp
func (b *Bwhatsapp) HandleError(err error) {
// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
if strings.Contains(err.Error(), "error processing data: received invalid data") {
// ignore tag 174 errors. https://github.com/42wim/matterbridge/issues/1094
if strings.Contains(err.Error(), "error processing data: received invalid data") || strings.Contains(err.Error(), "invalid string with tag 174") {
return
}
@@ -55,6 +56,7 @@ func (b *Bwhatsapp) reconnect(err error) {
err := b.conn.Restore()
if err == nil {
bf.Reset()
b.startedAt = uint64(time.Now().Unix())
return
}
}

View File

@@ -80,8 +80,33 @@ func (b *Bwhatsapp) getSenderName(senderJid string) string {
// if user is not in phone contacts
// it is the most obvious scenario unless you sync your phone contacts with some remote updated source
// users can change it in their WhatsApp settings -> profile -> click on Avatar
return sender.Notify
if sender.Notify != "" {
return sender.Notify
}
if sender.Short != "" {
return sender.Short
}
}
// try to reload this contact
_, err := b.conn.Contacts()
if err != nil {
b.Log.Errorf("error on update of contacts: %v", err)
}
if contact, exists := b.conn.Store.Contacts[senderJid]; exists {
// Add it to the user map
b.users[senderJid] = contact
if contact.Name != "" {
return contact.Name
}
// if user is not in phone contacts
// same as above
return contact.Notify
}
return ""
}

View File

@@ -67,6 +67,7 @@ func (b *Bwhatsapp) Connect() error {
// https://github.com/Rhymen/go-whatsapp#creating-a-connection
b.Log.Debugln("Connecting to WhatsApp..")
conn, err := whatsapp.NewConn(20 * time.Second)
conn.SetClientVersion(0, 4, 2080)
if err != nil {
return errors.New("failed to connect to WhatsApp: " + err.Error())
}

34
bridge/xmpp/handler.go Normal file
View File

@@ -0,0 +1,34 @@
package bxmpp
import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/matterbridge/go-xmpp"
)
// handleDownloadAvatar downloads the avatar of userid from channel
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
// logs an error message if it fails
func (b *Bxmpp) handleDownloadAvatar(avatar xmpp.AvatarData) {
rmsg := config.Message{
Username: "system",
Text: "avatar",
Channel: b.parseChannel(avatar.From),
Account: b.Account,
UserID: avatar.From,
Event: config.EventAvatarDownload,
Extra: make(map[string][]interface{}),
}
if _, ok := b.avatarMap[avatar.From]; !ok {
b.Log.Debugf("Avatar.From: %s", avatar.From)
err := helper.HandleDownloadSize(b.Log, &rmsg, avatar.From+".png", int64(len(avatar.Data)), b.General)
if err != nil {
b.Log.Error(err)
return
}
helper.HandleDownloadData(b.Log, &rmsg, avatar.From+".png", rmsg.Text, "", &avatar.Data, b.General)
b.Log.Debugf("Avatar download complete")
b.Remote <- rmsg
}
}

30
bridge/xmpp/helpers.go Normal file
View File

@@ -0,0 +1,30 @@
package bxmpp
import (
"regexp"
"github.com/42wim/matterbridge/bridge/config"
)
var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
// GetAvatar constructs a URL for a given user-avatar if it is available in the cache.
func getAvatar(av map[string]string, userid string, general *config.Protocol) string {
if hash, ok := av[userid]; ok {
// NOTE: This does not happen in bridge/helper/helper.go but messes up XMPP
id := pathRegex.ReplaceAllString(userid, "_")
return general.MediaServerDownload + "/" + hash + "/" + id + ".png"
}
return ""
}
func (b *Bxmpp) cacheAvatar(msg *config.Message) string {
fi := msg.Extra["file"][0].(config.FileInfo)
/* if we have a sha we have successfully uploaded the file to the media server,
so we can now cache the sha */
if fi.SHA != "" {
b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
b.avatarMap[msg.UserID] = fi.SHA
}
return ""
}

View File

@@ -23,12 +23,17 @@ type Bxmpp struct {
xmppMap map[string]string
connected bool
sync.RWMutex
avatarAvailability map[string]bool
avatarMap map[string]string
}
func New(cfg *bridge.Config) bridge.Bridger {
return &Bxmpp{
Config: cfg,
xmppMap: make(map[string]string),
Config: cfg,
xmppMap: make(map[string]string),
avatarAvailability: make(map[string]bool),
avatarMap: make(map[string]string),
}
}
@@ -67,8 +72,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
if msg.Event == config.EventMsgDelete {
return "", nil
}
b.Log.Debugf("=> Receiving %#v", msg)
if msg.Event == config.EventAvatarDownload {
return b.cacheAvatar(&msg), nil
}
// Make a action /me of the message, prepend the username with it.
// https://xmpp.org/extensions/xep-0245.html
if msg.Event == config.EventUserAction {
msg.Username = "/me " + msg.Username
}
// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
@@ -114,6 +130,9 @@ func (b *Bxmpp) createXMPP() error {
ServerName: strings.Split(b.GetString("Jid"), "@")[1],
InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
}
xmpp.DebugWriter = b.Log.Writer()
options := xmpp.Options{
Host: b.GetString("Server"),
User: b.GetString("Jid"),
@@ -122,7 +141,6 @@ func (b *Bxmpp) createXMPP() error {
StartTLS: true,
TLSConfig: tc,
Debug: b.GetBool("debug"),
Logger: b.Log.Writer(),
Session: true,
Status: "",
StatusMessage: "",
@@ -228,6 +246,16 @@ func (b *Bxmpp) handleXMPP() error {
event = config.EventTopicChange
}
available, sok := b.avatarAvailability[v.Remote]
avatar := ""
if !sok {
b.Log.Debugf("Requesting avatar data")
b.avatarAvailability[v.Remote] = false
b.xc.AvatarRequestData(v.Remote)
} else if available {
avatar = getAvatar(b.avatarMap, v.Remote, b.General)
}
msgID := v.ID
if v.ReplaceID != "" {
msgID = v.ReplaceID
@@ -237,6 +265,7 @@ func (b *Bxmpp) handleXMPP() error {
Text: v.Text,
Channel: b.parseChannel(v.Remote),
Account: b.Account,
Avatar: avatar,
UserID: v.Remote,
ID: msgID,
Event: event,
@@ -253,6 +282,10 @@ func (b *Bxmpp) handleXMPP() error {
b.Log.Debugf("<= Message is %#v", rmsg)
b.Remote <- rmsg
}
case xmpp.AvatarData:
b.handleDownloadAvatar(v)
b.avatarAvailability[v.From] = true
b.Log.Debugf("Avatar for %s is now available", v.From)
case xmpp.Presence:
// Do nothing.
}

View File

@@ -1,3 +1,114 @@
# v1.18.0
## New features
- nctalk: new protocol added. Add Nextcloud Talk support #1167
- general: Add an option to log into a file rather than stdout (#1168)
- api: Add websocket to API (#970)
## Enhancements
- telegram: Fix MarkdownV2 support in Telegram (#1169)
- whatsapp: Reload user information when a new contact is detected (whatsapp) (#1160)
- api: Add sane RemoteNickFormat default for API (#1157)
- irc: Skip gIRC built-in rate limiting (irc) (#1164)
- irc: Only colour IRC nicks if there is one. (#1161)
- docker: Combine runs to one layer (#1151)
## Bugfix
- general: Update dependencies for 1.18.0 release (#1175)
Discord users are encouraged to upgrade, this release works with the move to the discord.com domain.
This release couldn't exist without the following contributors:
@42wim, @jlu5, @qaisjp, @TheHolyRoger, @SuperSandro2000, @gary-kim, @z3bra, @greenx, @haykam821, @nathanaelhoun
# v1.17.5
## Enhancements
- irc: Add StripMarkdown option (irc). (#1145)
- general: Increase debug logging with function,file and linenumber (#1147)
- general: Update Dockerfile so inotify works (#1148)
- matrix: Add an option to disable sending HTML to matrix. Fixes #1022 (#1135)
- xmpp: Implement xep-0245 (xmpp). Closes #1137 (#1144)
## Bugfix
- discord: Fix #1120: replaceAction "_" crash (discord) (#1121)
- discord: Fix #1049: missing space before embeds (discord) (#1124)
- discord: Fix webhook EventUserAction messages being skipped (discord) (#1133)
- matrix: Avoid creating invalid url when the user doesn't have an avatar (matrix) (#1130)
- msteams: Ignore non-user messages (msteams). Fixes #1141 (#1149)
- slack: Do not use webhooks when token is configured (slack) (fixes #1123) (#1134)
- telegram: Fix forward from hidden users (telegram). Closes #1131 (#1143)
- xmpp: Prevent re-requesting avatar data (xmpp) (#1117)
This release couldn't exist without the following contributors:
@qaisjp, @xnaas, @42wim, @Polynomdivision, @tfve
# v1.17.4
## Bugfix
- general: Lowercase account names. Fixes #1108 (#1110)
- msteams: Remove panics and retry polling on failure (msteams). Fixes #1104 (#1105
- whatsapp: Update Rhymen/go-whatsapp. Fixes #1107 (#1109) (make whatsapp working again)
- discord: Add an ID cache (discord). Fixes #1106 (#1111) (fix delete/edits with webhooks)
# v1.17.3
## Enhancements
- xmpp: Implement User Avatar spoofing of XMPP users #1090
- rocketchat: Relay Joins/Topic changes in RocketChat bridge (#1085)
- irc: Add JoinDelay option (irc). Fixes #1084 (#1098)
- slack: Clip too long messages on 3000 length (slack). Fixes #1081 (#1102)
## Bugfix
- general: Fix the behavior of ShowTopicChange and SyncTopic (#1086)
- slack: Prevent image/message looping (slack). Fixes #1088 (#1096)
- whatsapp: Ignore non-critical errors (whatsapp). Fixes #1094 (#1100)
- irc: Add extra space before colon in attachments (irc). Fixes #1089 (#1101)
This release couldn't exist without the following contributors:
@42wim, @ldruschk, @qaisjp, @Polynomdivision
# v1.17.2
## Enhancements
- slack: Update vendor slack-go/slack (#1068)
- general: Update vendor d5/tengo (#1066)
- general: Clarify terminology used in mapping group chat IDs to channels in config (#1079)
## Bugfix
- whatsapp: Update Rhymen/go-whatsapp vendor and whatsapp version (#1078). Fixes Media upload #1074
- whatsapp: Reset start timestamp on reconnect (whatsapp). Fixes #1059 (#1064)
This release couldn't exist without the following contributors:
@42wim, @jheiselman
# v1.17.1
## Enhancements
- docker: Remove build dependencies from final image (multistage build) #1057
## Bugfix
- general: Don't transmit typing events from ourselves #1056
- general: Add support for build tags #1054
- discord: Strip extra info from emotes (discord) #1052
- msteams: fix macos build: Update vendor yaegashi/msgraph.go to v0.1.2 #1036
- whatsapp: Update client version whatsapp. Fixes #1061 #1062
This release couldn't exist without the following contributors:
@awigen, @qaisjp, @42wim
# v1.17.0
## New features

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -u -e -x -o pipefail
go version | grep go1.14 || exit
VERSION=$(git describe --tags)
mkdir ci/binaries
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux-amd64
GOOS=linux GOARCH=arm go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux-arm
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-darwin-amd64
cd ci
cat > deploy.json <<EOF
{
"package": {
"name": "Matterbridge",
"repo": "nightly",
"subject": "42wim"
},
"version": {
"name": "$VERSION"
},
"files":
[
{"includePattern": "ci/binaries/(.*)", "uploadPattern":"\$1"}
],
"publish": true
}
EOF

View File

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

View File

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

11
gateway/bridgemap/api.go Normal file
View File

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

View File

@@ -0,0 +1,12 @@
// +build !nodiscord
package bridgemap
import (
bdiscord "github.com/42wim/matterbridge/bridge/discord"
)
func init() {
FullMap["discord"] = bdiscord.New
UserTypingSupport["discord"] = struct{}{}
}

View File

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

11
gateway/bridgemap/birc.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
// +build !noslack
package bridgemap
import (
bslack "github.com/42wim/matterbridge/bridge/slack"
)
func init() {
FullMap["slack-legacy"] = bslack.NewLegacy
FullMap["slack"] = bslack.New
UserTypingSupport["slack"] = struct{}{}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -108,7 +108,7 @@ func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
func (gw *Gateway) checkConfig(cfg *config.Bridge) {
match := false
for _, key := range gw.Router.Config.Viper().AllKeys() {
if strings.HasPrefix(key, cfg.Account) {
if strings.HasPrefix(key, strings.ToLower(cfg.Account)) {
match = true
break
}

View File

@@ -169,7 +169,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
switch event {
case config.EventAvatarDownload:
// Avatar downloads are only relevant for telegram and mattermost for now
if dest.Protocol != "mattermost" && dest.Protocol != "telegram" {
if dest.Protocol != "mattermost" && dest.Protocol != "telegram" && dest.Protocol != "xmpp" {
return true
}
case config.EventJoinLeave:
@@ -179,7 +179,7 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
}
case config.EventTopicChange:
// only relay topic change when used in some way on other side
if dest.GetBool("ShowTopicChange") && dest.GetBool("SyncTopic") {
if !dest.GetBool("ShowTopicChange") && !dest.GetBool("SyncTopic") {
return true
}
}

57
go.mod
View File

@@ -5,34 +5,33 @@ require (
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0
github.com/Rhymen/go-whatsapp v0.1.0
github.com/d5/tengo/v2 v2.0.2
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/fsnotify/fsnotify v1.4.7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497
github.com/google/gops v0.3.6
github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334
github.com/d5/tengo/v2 v2.6.0
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.4.9
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725
github.com/google/gops v0.3.10
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
github.com/gorilla/schema v1.1.0
github.com/gorilla/websocket v1.4.1
github.com/hashicorp/golang-lru v0.5.3
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/golang-lru v0.5.4
github.com/hpcloud/tail v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e
github.com/labstack/echo/v4 v4.1.13
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da
github.com/labstack/echo/v4 v4.1.16
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
github.com/matterbridge/msgraph.go v0.0.0-20200308150230-9e043fe9dbaa
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
github.com/mattermost/mattermost-server v5.5.0+incompatible
github.com/mattn/go-runewidth v0.0.7 // indirect
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51
github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/missdeer/golib v1.0.3
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
@@ -45,22 +44,20 @@ require (
github.com/russross/blackfriday v1.5.2
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98
github.com/sirupsen/logrus v1.4.2
github.com/slack-go/slack v0.6.3-0.20200228121756-f56d616d5901
github.com/spf13/viper v1.6.1
github.com/stretchr/testify v1.4.0
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/sirupsen/logrus v1.6.0
github.com/slack-go/slack v0.6.5
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.5.1
github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
github.com/yaegashi/msgraph.go v0.1.3
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
golang.org/x/image v0.0.0-20200618115811-c13761719519
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
gomod.garykim.dev/nc-talk v0.0.1
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
//replace github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43
//replace github.com/yaegashi/msgraph.go => github.com/matterbridge/msgraph.go v0.0.0-20191226214848-9e5d9c08a4e1
go 1.13

328
go.sum
View File

@@ -1,58 +1,74 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557 h1:IZtuWGfzQnKnCSu+vl8WGLhpVQ5Uvy3rlSwqXSg+sQg=
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557/go.mod h1:jL0YSXMs/txjtGJ4PWrmETOk6KUHMDPMshgQZlTeB3Y=
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 h1:TO7d4rocnNFng6ZQrPe7U6WqHtK5eHEMrgrnnM/72IQ=
github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
github.com/Rhymen/go-whatsapp v0.1.0 h1:XTXhFIQ/fx9jKObUnUX2Q+nh58EyeHNhX7DniE8xeuA=
github.com/Rhymen/go-whatsapp v0.1.0/go.mod h1:xJSy+okeRjKkQEH/lEYrnekXB3PG33fqL0I6ncAkV50=
github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334 h1:kb1zvD+xd+XbPUdQ0lMxnRaQ76N5C9vMAClLi8Dyw1Y=
github.com/Rhymen/go-whatsapp v0.1.1-0.20200421062035-31e8111ac334/go.mod h1:o7jjkvKnigfu432dMbQ/w4PH0Yp5u4Y6ysCNjUlcYCk=
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/d5/tengo/v2 v2.0.2 h1:3APkPZPc1FExaJoWrN5YzvDqc6GNkQH6ehmCRDmN83I=
github.com/d5/tengo/v2 v2.0.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/d5/tengo/v2 v2.6.0 h1:D0cJtpiBzaLJ/Smv6nnUc/LIfO46oKwDx85NZtIRNRI=
github.com/d5/tengo/v2 v2.6.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX87a2zVnnbPPickIM9Gf9OIhsIgWQ=
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81 h1:FdZThbRF0R+2qgyBl3KCVNWWBmKm68E+stT3rnQ02Ww=
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81/go.mod h1:lDm2E64X4OjFdBUA4hlN4mEvbSitvhJdKw7rsA8KHgI=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -60,34 +76,64 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497 h1:wJkj+x9gPYlDyM34C6r3SXPs270coWeh85wu1CsusDo=
github.com/gomarkdown/markdown v0.0.0-20200127000047-1813ea067497/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725 h1:X6sZdr+t2E2jwajTy/FfXbmAKPFTYxEq9hiFgzMiuPQ=
github.com/gomarkdown/markdown v0.0.0-20200609195525-3f9352745725/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to=
github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gops v0.3.10 h1:M2XZYgfUW+P7AOCLiu4CRb0rQfwnslLyB4B9Mp0vXmE=
github.com/google/gops v0.3.10/go.mod h1:38bMPVKFh+1X106CPpbLAWtZIR1+xwgzT9gew0kn6w4=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -95,71 +141,82 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e h1:KbPTfR/PYuau1IzKoE4lnd8yby5I2pBj+VR6fSVbYU8=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200226211841-4e48f3eaef3e/go.mod h1:vNc28YFzigVJod0j5EbuTtRIe7swx8vodh2yA4jZ2s8=
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999 h1:2d+FLQbz4xRTi36DO1qYNUwfORax9XcQ0jhbO81Vago=
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da h1:LK+8uBG3kNikj664cjFt88RBmuGmonxkXv2rUVfbqz4=
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da/go.mod h1:xJA+X9ZVyT/irGldcb7q1XnJBq5F9s5H9h2L44Y+poY=
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/echo/v4 v4.1.13 h1:JYgKq6NQQSaKbQcsOadAKX1kUVLCUzLGwu8sxN5tC34=
github.com/labstack/echo/v4 v4.1.13/go.mod h1:3WZNypykZ3tnqpF2Qb4fPg27XDunFqgP3HGDmCMgv7U=
github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o=
github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7 h1:BS9tqL0OCiOGuy/CYYk2gc33fxqaqh5/rhqMKu4tcYA=
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7/go.mod h1:liX5MxHPrwgHaKowoLkYGwbXfYABh1jbZ6FpElbGF1I=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3 h1:VP/DNRn2HtrVRN6+X3h4FDcQI2OOKT+88WUi21ZD1Kw=
github.com/matterbridge/discordgo v0.18.1-0.20200308151012-aa40f01cbcc3/go.mod h1:5a1bHtG/38ofcx9cgwM5eTW/Pl4SpbQksNDnTRcGA2Y=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048 h1:B9HaistmV+MD8/33BXmZe1zPIn+RImAFVXNNSOrwU2E=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20200411204219-d5c18ce75048/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d h1:NBckP4nw7qVspbt7cOZYsrOrEbq7tATdMjSjc1hW63A=
github.com/matterbridge/discordgo v0.21.2-0.20200718144317-01fe5db6c78d/go.mod h1:411nZYv0UMMrtppR5glXop1foboJiFAowy+42U+Ahvw=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4=
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k=
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050 h1:kWkP1lXpkvtoNL08jkP3XQH/zvDOEXJpdCJd/DlIvMw=
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6 h1:Kl65VJv38HjYFnnwH+MP6Z8hcJT5UHuSpHVU5vW1HH0=
github.com/matterbridge/gomatrix v0.0.0-20200209224845-c2104d7936a6/go.mod h1:+jWeaaUtXQbBRdKYWfjW6JDDYiI2XXE+3NnTjW5kg8g=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtWGtfTgZVxHG1lcKjv+re7dRwyyuYFNu69xdho=
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
github.com/matterbridge/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/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba h1:XleOY4IjAEIcxAh+IFwT5JT5Ze3RHiYz6m+4ZfZ0rc0=
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51 h1:MpI7hy3MiCnrggmZI/s8LaPbLVOOWpzDbjA4F+XaXaM=
github.com/mattn/godown v0.0.0-20180312012330-2e9e17e0ea51/go.mod h1:s3KUdOIXJ+jaGM++XHiXA6gikdleaWVATCcQGD4h734=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561 h1:0YGo77enc6tJpXQxUeQWs9bPIQPTH1lbOmc5tgRuq4o=
github.com/mattn/godown v0.0.0-20200217152941-afc959f6a561/go.mod h1:/ivCKurgV/bx6yqtP/Jtc2Xmrv3beCYBvlfAUl4X5g4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/missdeer/golib v1.0.3 h1:+kz/tn1lXlPS8i+gjHHVAZC8YcgrmfiMTqELyvOwI4g=
github.com/missdeer/golib v1.0.3/go.mod h1:Cys1ITPPZxIk2eTcQcKT3jDsBdhICAfrrw+ki/eRXxA=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monaco-io/request v1.0.3 h1:FsiIwXCCbHEyWx9A7lgg6JBTMHhHlEEsADsgAOvZ9HA=
github.com/monaco-io/request v1.0.3/go.mod h1:EmggwHktBsbJmCgwZXqy7o0H1NNsAstQBWZrFVd3xtQ=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 h1:oKIteTqeSpenyTrOVj5zkiyCaflLa8B+CD0324otT+o=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9DtP8gnTmNtMxbPyhYltfxsITel8g=
@@ -174,6 +231,7 @@ github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw=
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4=
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcUhvI17yO9SlpAdDIJGkhDEgO8A=
@@ -185,6 +243,7 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -194,26 +253,32 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
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/rickb777/date v1.12.4 h1:+6IzcCCS/1t17DrmnEvrznyq7nM8vPwir6/UhlyohKw=
github.com/rickb777/date v1.12.4/go.mod h1:xP0eo/I5qmUt97yRGClHZfyLZ3ikMw6v6SU5MOGZTE0=
github.com/rickb777/plural v1.2.0 h1:5tvEc7UBCZ7l8h/2UeybSkt/uu1DQsZFOFdNevmUhlE=
github.com/rickb777/plural v1.2.0/go.mod h1:UdpyWFCGbo3mvK3f/PfZOAOrkjzJlYN/sD46XNWJ+Es=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98 h1:sN07ff+PSRsUNhpSod4uGKAQ+Nc0FXsBPG9FmYMNg4w=
github.com/shazow/ssh-chat v1.8.3-0.20200308224626-80ddf1f43a98/go.mod h1:xkTgfD+WP+KR4HuG76oal25BBEeu5kJyi2EOsgiu/4Q=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shirou/gopsutil v2.20.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/slack-go/slack v0.6.3-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/slack-go/slack v0.6.5 h1:IkDKtJ2IROJNoe3d6mW870/NRKvq2fhLB/Q5XmzWk00=
github.com/slack-go/slack v0.6.5/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -228,32 +293,40 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447 h1:CHgPZh8bFkZmislPrr/0gd7MciDAX+JJB70A2/5Lvmo=
github.com/zfjagann/golang-ring v0.0.0-20190106091943-a88bb6aef447/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg=
github.com/yaegashi/msgraph.go v0.1.3 h1:xeknrGbPGqUVvjjtXGuxvesHLNJ6jEfiOtqJToZe0qw=
github.com/yaegashi/msgraph.go v0.1.3/go.mod h1:dpty8G9hMEC1xBQeXp6Z2hCXKalqczk2BLvK9me/TUU=
github.com/yaegashi/wtz.go v0.0.2/go.mod h1:nOLA5QXsmdkRxBkP5tljhua13ADHCKirLBrzPf4PEJc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 h1:UQwvu7FjUEdVYofx0U6bsc5odNE7wa5TSA0fl559GcA=
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@@ -262,67 +335,149 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20171017063910-8dbc5d05d6ed/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200529172331-a64b76657301/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomod.garykim.dev/nc-talk v0.0.1 h1:6mgjcAf5/HMkV0CFGeXVfYHG7FAUCQcGR8eg9oM6fCc=
gomod.garykim.dev/nc-talk v0.0.1/go.mod h1:0/Ksg0osAYmnWKs1OcCG+gBQ4HU1xiF1699g9B6jWZw=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
@@ -335,7 +490,12 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=

View File

@@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"os"
"runtime"
"strings"
"github.com/42wim/matterbridge/bridge/config"
@@ -15,7 +16,7 @@ import (
)
var (
version = "1.17.0"
version = "1.18.0"
githash string
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
@@ -50,6 +51,15 @@ func main() {
cfg := config.NewConfig(rootLogger, *flagConfig)
cfg.BridgeValues().General.Debug = *flagDebug
// if logging to a file, ensure it is closed when the program terminates
// nolint:errcheck
defer func() {
if f, ok := rootLogger.Out.(*os.File); ok {
f.Sync()
f.Close()
}
}()
r, err := gateway.NewRouter(rootLogger, cfg, bridgemap.FullMap)
if err != nil {
logger.Fatalf("Starting gateway failed: %s", err)
@@ -67,17 +77,31 @@ func setupLogger() *logrus.Logger {
Formatter: &prefixed.TextFormatter{
PrefixPadding: 13,
DisableColors: true,
FullTimestamp: true,
},
Level: logrus.InfoLevel,
}
if *flagDebug || os.Getenv("DEBUG") == "1" {
logger.SetReportCaller(true)
logger.Formatter = &prefixed.TextFormatter{
PrefixPadding: 13,
DisableColors: true,
FullTimestamp: false,
ForceFormatting: true,
PrefixPadding: 13,
DisableColors: true,
FullTimestamp: false,
CallerFormatter: func(function, file string) string {
return fmt.Sprintf(" [%s:%s]", function, file)
},
CallerPrettyfier: func(f *runtime.Frame) (string, string) {
sp := strings.SplitAfter(f.File, "/matterbridge/")
filename := f.File
if len(sp) > 1 {
filename = sp[1]
}
s := strings.Split(f.Function, ".")
funcName := s[len(s)-1]
return funcName, fmt.Sprintf("%s:%d", filename, f.Line)
},
}
logger.Level = logrus.DebugLevel
logger.WithFields(logrus.Fields{"prefix": "main"}).Info("Enabling debug logging.")
}

View File

@@ -103,6 +103,10 @@ ColorNicks=false
#OPTIONAL (default empty)
RunCommands=["PRIVMSG user hello","PRIVMSG chanserv something"]
#StripMarkdown strips markdown from messages
#OPTIONAL (default false)
StripMarkdown=false
#Nicks you want to ignore.
#Regular expressions supported
#Messages from those users will not be sent to other bridges.
@@ -177,6 +181,12 @@ StripNick=false
#OPTIONAL (default false)
ShowTopicChange=false
#Delay in milliseconds between channel joins
#Only useful when you have a LOT of channels to join
#See https://github.com/42wim/matterbridge/issues/1084
#OPTIONAL (default 0)
JoinDelay=0
###################################################################
#XMPP section
###################################################################
@@ -545,6 +555,96 @@ Label=""
# REQUIRED
Team="myteam"
###################################################################
# Microsoft teams section
# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup
###################################################################
[msteams.myteam]
# TenantID
# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup#get-necessary-ids-for-matterbridge
TenantID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# ClientID
# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup#get-necessary-ids-for-matterbridge
ClientID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# TeamID
# See https://github.com/42wim/matterbridge/wiki/MS-Teams-setup#get-necessary-ids-for-matterbridge
TeamID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
## RELOADABLE SETTINGS
## Settings below can be reloaded by editing the file
#Nicks you want to ignore.
#Regular expressions supported
#Messages from those users will not be sent to other bridges.
#OPTIONAL
IgnoreNicks="ircspammer1 ircspammer2"
#Messages you want to ignore.
#Messages matching these regexp will be ignored and not sent to other bridges
#See https://regex-golang.appspot.com/assets/html/index.html for more regex info
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
IgnoreMessages="^~~ badword"
#messages you want to replace.
#it replaces outgoing messages from the bridge.
#so you need to place it by the sending bridge definition.
#regular expressions supported
#some examples:
#this replaces cat => dog and sleep => awake
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
#this replaces every number with number. 123 => numbernumbernumber
#replacemessages=[ ["[0-9]","number"] ]
#optional (default empty)
ReplaceMessages=[ ["cat","dog"] ]
#nicks you want to replace.
#see replacemessages for syntaxa
#optional (default empty)
ReplaceNicks=[ ["user--","user"] ]
#Extractnicks is used to for example rewrite messages from other relaybots
#See https://github.com/42wim/matterbridge/issues/713 and https://github.com/42wim/matterbridge/issues/466
#some examples:
#this replaces a message like "Relaybot: <relayeduser> something interesting" to "relayeduser: something interesting"
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ]
#you can use multiple entries for multiplebots
#this also replaces a message like "otherbot: (relayeduser) something else" to "relayeduser: something else"
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ]
#OPTIONAL (default empty)
ExtractNicks=[ ["otherbot","<(.*?)>\\s+" ] ]
#extra label that can be used in the RemoteNickFormat
#optional (default empty)
Label=""
#RemoteNickFormat defines how remote users appear on this bridge
#See [general] config section for default options
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
#Enable to show users joins/parts from other bridges
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
#OPTIONAL (default false)
ShowJoinPart=false
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
#It will strip other characters from the nick
#OPTIONAL (default false)
StripNick=false
#Enable to show topic changes from other bridges
#Only works hiding/show topic changes from slack bridge for now
#OPTIONAL (default false)
ShowTopicChange=false
#Opportunistically preserve threaded replies between bridges
#that support threading
#OPTIONAL (default false)
PreserveThreading=false
###################################################################
#slack section
###################################################################
@@ -1113,6 +1213,11 @@ Password="yourpass"
#OPTIONAL (default false)
NoHomeServerSuffix=false
#Whether to disable sending of HTML content to matrix
#See https://github.com/42wim/matterbridge/issues/1022
#OPTIONAL (default false)
HTMLDisable=false
## RELOADABLE SETTINGS
## Settings below can be reloaded by editing the file
@@ -1278,7 +1383,22 @@ StripNick=false
#OPTIONAL (default false)
ShowTopicChange=false
###################################################################
#
# NCTalk (Nextcloud Talk)
#
###################################################################
[nctalk.bridge]
# Url of your Nextcloud server
Server = "https://cloud.youdomain.me"
# Username of the bot
Login = "talkuser"
# Password of the bot
Password = "talkuserpass"
###################################################################
#
@@ -1499,6 +1619,14 @@ MediaDownloadBlacklist=[".html$",".htm$"]
#OPTIONAL (default false)
IgnoreFailureOnStart=false
#LogFile defines the location of a file to write logs into, rather
#than stdout.
#Logging will still happen on stdout if the file cannot be open for
#writing, or if the value is empty. Note that the log won't roll, so
#you might want to use logrotate(8) with this feature.
#OPTIONAL (default empty)
LogFile=/var/log/matterbridge.log
###################################################################
#Tengo configuration
###################################################################
@@ -1589,32 +1717,46 @@ enable=true
# REQUIRED
account="irc.freenode"
# channel to connect on that account
# How to specify them for the different bridges:
# The channel key in each gateway is mapped to a similar group chat ID on the chat platform
# To find the group chat ID for different platforms, refer to the table below
#
# irc - #channel (# is required) (this needs to be lowercase!)
# mattermost - channel (the channel name as seen in the URL, not the displayname)
# gitter - username/room
# xmpp - channel
# slack - channel (without the #)
# - ID:C123456 (where C123456 is the channel ID) does not work with webhook
# discord - channel (without the #)
# - ID:123456789 (where 123456789 is the channel ID)
# (https://github.com/42wim/matterbridge/issues/57)
# - category/channel (without the #) if you're using discord categories to group your channels
# telegram - chatid (a large negative number, eg -123456789)
# see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau)
# hipchat - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel)
# rocketchat - #channel (# is required (also needed for private channels!)
# matrix - #channel:server (eg #yourchannel:matrix.org)
# - encrypted rooms are not supported in matrix
# steam - chatid (a large number).
# The number in the URL when you click "enter chat room" in the browser
# whatsapp - 48111222333-123455678999@g.us A unique group JID;
# if you specify an empty string bridge will list all the possibilities
# - "Group Name" if you specify a group name the bridge will hint its JID to specify
# as group names might change in time and contain weird emoticons
# zulip - stream/topic:topicname (without the #)
# Platform | Identifier name | Example | Description
# -------------------------------------------------------------------------------------------------------------------------------------
# | channel | general | Do not include the # symbol
# discord | channel id | ID:123456789 | See https://github.com/42wim/matterbridge/issues/57
# | category/channel | Media/gaming | Without # symbol. If you're using discord categories to group your channels
# -------------------------------------------------------------------------------------------------------------------------------------
# gitter | username/room | general | As seen in the gitter.im URL
# -------------------------------------------------------------------------------------------------------------------------------------
# hipchat | id_channel | example needed | See https://www.hipchat.com/account/xmpp for the correct channel
# -------------------------------------------------------------------------------------------------------------------------------------
# irc | channel | #general | The # symbol is required and should be lowercase!
# -------------------------------------------------------------------------------------------------------------------------------------
# mattermost | channel | general | This is the channel name as seen in the URL, not the display name
# -------------------------------------------------------------------------------------------------------------------------------------
# matrix | #channel:server | #yourchannel:matrix.org | Encrypted rooms are not supported in matrix
# -------------------------------------------------------------------------------------------------------------------------------------
# msteams | threadId | 19:82abcxx@thread.skype | You'll find the threadId in the URL
# -------------------------------------------------------------------------------------------------------------------------------------
# rocketchat | channel | #channel | # is required for private channels too
# -------------------------------------------------------------------------------------------------------------------------------------
# slack | channel name | general | Do not include the # symbol
# | channel id | ID:C123456 | The underlying ID of a channel. This doesn't work with
# -------------------------------------------------------------------------------------------------------------------------------------
# steam | chatid | example needed | The number in the URL when you click "enter chat room" in the browser
# -------------------------------------------------------------------------------------------------------------------------------------
# nctalk | token | xs25tz5y | The token in the URL when you are in a chat. It will be the last part of the URL.
# -------------------------------------------------------------------------------------------------------------------------------------
# telegram | chatid | -123456789 | A large negative number. see https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
# -------------------------------------------------------------------------------------------------------------------------------------
# whatsapp | group JID | 48111222333-123455678999@g.us | A unique group JID. If you specify an empty string, bridge will list all the possibilities
# | "Group Name" | "Family Chat" | if you specify a group name, the bridge will find hint the JID to specify. Names can change over time and are not stable.
# -------------------------------------------------------------------------------------------------------------------------------------
# xmpp | channel | general | The room name
# -------------------------------------------------------------------------------------------------------------------------------------
# zulip | stream/topic:topic | general/off-topic:food | Do not use the # when specifying a topic
# -------------------------------------------------------------------------------------------------------------------------------------
#
# REQUIRED
channel="#testing"

File diff suppressed because it is too large Load Diff

View File

@@ -56,6 +56,8 @@ message Location {
}
message Point {
optional int32 xDeprecated = 1;
optional int32 yDeprecated = 2;
optional double x = 3;
optional double y = 4;
}
@@ -93,6 +95,7 @@ message ContextInfo {
optional AdReplyInfo quotedAd = 23;
optional MessageKey placeholderKey = 24;
optional uint32 expiration = 25;
optional int64 ephemeralSettingTimestamp = 26;
}
message SenderKeyDistributionMessage {
@@ -136,6 +139,11 @@ message LocationMessage {
optional string name = 3;
optional string address = 4;
optional string url = 5;
optional bool isLive = 6;
optional uint32 accuracyInMeters = 7;
optional float speedInMps = 8;
optional uint32 degreesClockwiseFromMagneticNorth = 9;
optional string comment = 11;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@@ -238,9 +246,29 @@ message ProtocolMessage {
enum PROTOCOL_MESSAGE_TYPE {
REVOKE = 0;
EPHEMERAL_SETTING = 3;
EPHEMERAL_SYNC_RESPONSE = 4;
HISTORY_SYNC_NOTIFICATION = 5;
}
optional PROTOCOL_MESSAGE_TYPE type = 2;
optional uint32 ephemeralExpiration = 4;
optional int64 ephemeralSettingTimestamp = 5;
optional HistorySyncNotification historySyncNotification = 6;
}
message HistorySyncNotification {
optional bytes fileSha256 = 1;
optional uint64 fileLength = 2;
optional bytes mediaKey = 3;
optional bytes fileEncSha256 = 4;
optional string directPath = 5;
enum HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE {
INITIAL_BOOTSTRAP = 0;
INITIAL_STATUS_V3 = 1;
FULL = 2;
RECENT = 3;
}
optional HISTORY_SYNC_NOTIFICATION_HISTORYSYNCTYPE syncType = 6;
optional uint32 chunkOrder = 7;
}
message ContactsArrayMessage {
@@ -355,6 +383,8 @@ message StickerMessage {
optional int64 mediaKeyTimestamp = 10;
optional uint32 firstFrameLength = 11;
optional bytes firstFrameSidecar = 12;
optional bool isAnimated = 13;
optional bytes pngThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@@ -401,6 +431,12 @@ message TemplateButtonReplyMessage {
optional uint32 selectedIndex = 4;
}
message CatalogSnapshot {
optional ImageMessage catalogImage = 1;
optional string title = 2;
optional string description = 3;
}
message ProductSnapshot {
optional ImageMessage productImage = 1;
optional string productId = 2;
@@ -417,6 +453,7 @@ message ProductSnapshot {
message ProductMessage {
optional ProductSnapshot product = 1;
optional string businessOwnerJid = 2;
optional CatalogSnapshot catalog = 4;
optional ContextInfo contextInfo = 17;
}
@@ -513,6 +550,8 @@ message WebFeatures {
optional WEB_FEATURES_FLAG templateMessage = 30;
optional WEB_FEATURES_FLAG templateMessageInteractivity = 31;
optional WEB_FEATURES_FLAG ephemeralMessages = 32;
optional WEB_FEATURES_FLAG e2ENotificationSync = 33;
optional WEB_FEATURES_FLAG recentStickersV2 = 34;
}
message TabletNotificationsInfo {
@@ -537,6 +576,11 @@ message WebNotificationsInfo {
}
message PaymentInfo {
enum PAYMENT_INFO_CURRENCY {
UNKNOWN_CURRENCY = 0;
INR = 1;
}
optional PAYMENT_INFO_CURRENCY currencyDeprecated = 1;
optional uint64 amount1000 = 2;
optional string receiverJid = 3;
enum PAYMENT_INFO_STATUS {
@@ -559,6 +603,37 @@ message PaymentInfo {
optional uint64 expiryTimestamp = 7;
optional bool futureproofed = 8;
optional string currency = 9;
enum PAYMENT_INFO_TXNSTATUS {
UNKNOWN = 0;
PENDING_SETUP = 1;
PENDING_RECEIVER_SETUP = 2;
INIT = 3;
SUCCESS = 4;
COMPLETED = 5;
FAILED = 6;
FAILED_RISK = 7;
FAILED_PROCESSING = 8;
FAILED_RECEIVER_PROCESSING = 9;
FAILED_DA = 10;
FAILED_DA_FINAL = 11;
REFUNDED_TXN = 12;
REFUND_FAILED = 13;
REFUND_FAILED_PROCESSING = 14;
REFUND_FAILED_DA = 15;
EXPIRED_TXN = 16;
AUTH_CANCELED = 17;
AUTH_CANCEL_FAILED_PROCESSING = 18;
AUTH_CANCEL_FAILED = 19;
COLLECT_INIT = 20;
COLLECT_SUCCESS = 21;
COLLECT_FAILED = 22;
COLLECT_FAILED_RISK = 23;
COLLECT_REJECTED = 24;
COLLECT_EXPIRED = 25;
COLLECT_CANCELED = 26;
COLLECT_CANCELLING = 27;
}
optional PAYMENT_INFO_TXNSTATUS txnStatus = 10;
}
message WebMessageInfo {
@@ -668,4 +743,5 @@ message WebMessageInfo {
optional PaymentInfo quotedPaymentInfo = 31;
optional uint64 ephemeralStartTimestamp = 32;
optional uint32 ephemeralDuration = 33;
}
}

View File

@@ -90,6 +90,7 @@ type Conn struct {
longClientName string
shortClientName string
clientVersion string
loginSessionLock sync.RWMutex
Proxy func(*http.Request) (*url.URL, error)
@@ -121,6 +122,7 @@ func NewConn(timeout time.Duration) (*Conn, error) {
longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp",
clientVersion: "0.1.0",
}
return wac, wac.connect()
}
@@ -135,6 +137,7 @@ func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL
longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp",
clientVersion: "0.1.0",
Proxy: proxy,
}
return wac, wac.connect()

View File

@@ -6,7 +6,7 @@ require (
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect
github.com/golang/protobuf v1.3.0
github.com/gorilla/websocket v1.4.0
github.com/gorilla/websocket v1.4.1
github.com/pkg/errors v0.8.1
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
)

View File

@@ -12,8 +12,9 @@ github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=

View File

@@ -10,10 +10,8 @@ import (
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"strings"
"net/url"
"time"
"github.com/Rhymen/go-whatsapp/crypto/cbc"
@@ -95,7 +93,50 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) {
return data[:n-10], data[n-10 : n], nil
}
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
type MediaConn struct {
Status int `json:"status"`
MediaConn struct {
Auth string `json:"auth"`
TTL int `json:"ttl"`
Hosts []struct {
Hostname string `json:"hostname"`
IPs []string `json:"ips"`
} `json:"hosts"`
} `json:"media_conn"`
}
func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) {
queryReq := []interface{}{"query", "mediaConn"}
ch, err := wac.writeJson(queryReq)
if err != nil {
return "", "", 0, err
}
var resp MediaConn
select {
case r := <-ch:
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err)
}
case <-time.After(wac.msgTimeout):
return "", "", 0, fmt.Errorf("query media conn timed out")
}
if resp.Status != 200 {
return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status)
}
return resp.MediaConn.Hosts[0].Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil
}
var mediaTypeMap = map[MediaType]string{
MediaImage: "/mms/image",
MediaVideo: "/mms/video",
MediaDocument: "/mms/document",
MediaAudio: "/mms/audio",
}
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
data, err := ioutil.ReadAll(reader)
if err != nil {
return "", nil, nil, nil, 0, err
@@ -128,67 +169,30 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK
sha.Write(append(enc, mac...))
fileEncSha256 = sha.Sum(nil)
var filetype string
switch appInfo {
case MediaImage:
filetype = "image"
case MediaAudio:
filetype = "audio"
case MediaDocument:
filetype = "document"
case MediaVideo:
filetype = "video"
hostname, auth, _, err := wac.queryMediaConn()
token := base64.URLEncoding.EncodeToString(fileEncSha256)
q := url.Values{
"auth": []string{auth},
"token": []string{token},
}
path := mediaTypeMap[appInfo]
uploadURL := url.URL{
Scheme: "https",
Host: hostname,
Path: fmt.Sprintf("%s/%s", path, token),
RawQuery: q.Encode(),
}
uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
ch, err := wac.writeJson(uploadReq)
body := bytes.NewReader(append(enc, mac...))
req, err := http.NewRequest("POST", uploadURL.String(), body)
if err != nil {
return "", nil, nil, nil, 0, err
}
var resp map[string]interface{}
select {
case r := <-ch:
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v", err)
}
case <-time.After(wac.msgTimeout):
return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out")
}
if int(resp["status"].(float64)) != 200 {
return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"])
}
var b bytes.Buffer
w := multipart.NewWriter(&b)
hashWriter, err := w.CreateFormField("hash")
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256)))
fileWriter, err := w.CreateFormFile("file", "blob")
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
io.Copy(fileWriter, bytes.NewReader(append(enc, mac...)))
err = w.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
req, err := http.NewRequest("POST", resp["url"].(string), &b)
if err != nil {
return "", nil, nil, nil, 0, err
}
req.Header.Set("Content-Type", w.FormDataContentType())
req.Header.Set("Origin", "https://web.whatsapp.com")
req.Header.Set("Referer", "https://web.whatsapp.com/")
req.URL.Query().Set("f", "j")
client := &http.Client{}
// Submit the request
res, err := client.Do(req)

View File

@@ -15,7 +15,10 @@ import (
)
func (wac *Conn) readPump() {
defer wac.wg.Done()
defer func() {
wac.wg.Done()
_, _ = wac.Disconnect()
}()
var readErr error
var msgType int
@@ -31,7 +34,6 @@ func (wac *Conn) readPump() {
case <-readerFound:
if readErr != nil {
wac.handle(&ErrConnectionFailed{Err: readErr})
_, _ = wac.Disconnect()
return
}
msg, err := ioutil.ReadAll(reader)

View File

@@ -18,7 +18,7 @@ import (
)
//represents the WhatsAppWeb client version
var waVersion = []int{0, 3, 3324}
var waVersion = []int{0, 4, 2080}
/*
Session contains session individual information. To be able to resume the connection without scanning the qr code
@@ -107,10 +107,10 @@ func CheckCurrentServerVersion() ([]int, error) {
}
b64ClientId := base64.StdEncoding.EncodeToString(clientId)
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true}
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true}
loginChan, err := wac.writeJson(login)
if err != nil {
return nil, fmt.Errorf("error writing login", err)
return nil, fmt.Errorf("error writing login: %s", err.Error())
}
// Retrieve an answer from the websocket
@@ -123,7 +123,7 @@ func CheckCurrentServerVersion() ([]int, error) {
var resp map[string]interface{}
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return nil, fmt.Errorf("error decoding login", err)
return nil, fmt.Errorf("error decoding login: %s", err.Error())
}
// Take the curr property as X.Y.Z and split it into as int slice
@@ -141,17 +141,17 @@ func CheckCurrentServerVersion() ([]int, error) {
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
*/
func (wac *Conn) SetClientName(long, short string) error {
func (wac *Conn) SetClientName(long, short, version string) error {
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
return fmt.Errorf("cannot change client name after logging in")
}
wac.longClientName, wac.shortClientName = long, short
wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version
return nil
}
/*
SetClientVersion sets WhatsApp client version
Default value is 0.3.3324
Default value is 0.4.2080
*/
func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
waVersion = []int{major, minor, patch}
@@ -213,7 +213,7 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
}
session.ClientId = base64.StdEncoding.EncodeToString(clientId)
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true}
loginChan, err := wac.writeJson(login)
if err != nil {
return session, fmt.Errorf("error writing login: %v\n", err)
@@ -369,7 +369,7 @@ func (wac *Conn) Restore() error {
wac.listener.Unlock()
//admin init
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true}
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true}
initChan, err := wac.writeJson(init)
if err != nil {
return fmt.Errorf("error writing admin init: %v\n", err)

View File

@@ -11,9 +11,10 @@ builds:
- darwin
- linux
- windows
archive:
files:
- none*
archives:
-
files:
- none*
checksum:
name_template: 'checksums.txt'
changelog:

View File

@@ -6,6 +6,7 @@ lint:
test: generate lint
go test -race -cover ./...
go run ./cmd/tengo -resolve ./testdata/cli/test.tengo
fmt:
go fmt ./...

View File

@@ -5,9 +5,8 @@
# The Tengo Language
[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo)
![test](https://github.com/d5/tengo/workflows/test/badge.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/d5/tengo)](https://goreportcard.com/report/github.com/d5/tengo)
[![CircleCI](https://circleci.com/gh/d5/tengo.svg?style=svg)](https://circleci.com/gh/d5/tengo)
[![Sourcegraph](https://sourcegraph.com/github.com/d5/tengo/-/badge.svg)](https://sourcegraph.com/github.com/d5/tengo?badge)
**Tengo is a small, dynamic, fast, secure script language for Go.**
@@ -52,19 +51,21 @@ fmt.println(sum("", [1, 2, 3])) // "123"
## Benchmark
| | fib(35) | fibt(35) | Type |
| | fib(35) | fibt(35) | Language (Type) |
| :--- | ---: | ---: | :---: |
| Go | `48ms` | `3ms` | Go (native) |
| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go |
| Lua | `1,416ms` | `3ms` | Lua (native) |
| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go |
| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go |
| Python | `2,588ms` | `26ms` | Python (native) |
| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go |
| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go |
| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go |
| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go |
| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go |
| [**Tengo**](https://github.com/d5/tengo) | `2,931ms` | `4ms` | Tengo (VM) |
| [go-lua](https://github.com/Shopify/go-lua) | `4,824ms` | `4ms` | Lua (VM) |
| [GopherLua](https://github.com/yuin/gopher-lua) | `5,365ms` | `4ms` | Lua (VM) |
| [goja](https://github.com/dop251/goja) | `5,533ms` | `5ms` | JavaScript (VM) |
| [starlark-go](https://github.com/google/starlark-go) | `11,495ms` | `5ms` | Starlark (Interpreter) |
| [Yaegi](https://github.com/containous/yaegi) | `15,645ms` | `12ms` | Yaegi (Interpreter) |
| [gpython](https://github.com/go-python/gpython) | `16,322ms` | `5ms` | Python (Interpreter) |
| [otto](https://github.com/robertkrimen/otto) | `73,093ms` | `10ms` | JavaScript (Interpreter) |
| [Anko](https://github.com/mattn/anko) | `79,809ms` | `8ms` | Anko (Interpreter) |
| - | - | - | - |
| Go | `53ms` | `3ms` | Go (Native) |
| Lua | `1,612ms` | `3ms` | Lua (Native) |
| Python | `2,632ms` | `23ms` | Python 2 (Native) |
_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo):
Fibonacci(35)_
@@ -75,6 +76,10 @@ _* See [here](https://github.com/d5/tengobench) for commands/codes used_
## Quick Start
```
go get github.com/d5/tengo/v2
```
A simple Go example code that compiles/runs Tengo script code with some input/output values:
```golang
@@ -133,3 +138,10 @@ each([a, b, c, d], func(x) {
- [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md)
- [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md)
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)
- Syntax Highlighters: [VSCode](https://github.com/lissein/vscode-tengo), [Atom](https://github.com/d5/tengo-atom)
- **Why the name Tengo?** It's from [1Q84](https://en.wikipedia.org/wiki/1Q84).
##
:hearts: Like writing Go code? Come work at Skool. [We're hiring!](https://jobs.lever.co/skool)

View File

@@ -13,6 +13,14 @@ var builtinFuncs = []*BuiltinFunction{
Name: "append",
Value: builtinAppend,
},
{
Name: "delete",
Value: builtinDelete,
},
{
Name: "splice",
Value: builtinSplice,
},
{
Name: "string",
Value: builtinString,
@@ -500,3 +508,104 @@ func builtinAppend(args ...Object) (Object, error) {
}
}
}
// builtinDelete deletes Map keys
// usage: delete(map, "key")
// key must be a string
func builtinDelete(args ...Object) (Object, error) {
argsLen := len(args)
if argsLen != 2 {
return nil, ErrWrongNumArguments
}
switch arg := args[0].(type) {
case *Map:
if key, ok := args[1].(*String); ok {
delete(arg.Value, key.Value)
return UndefinedValue, nil
}
return nil, ErrInvalidArgumentType{
Name: "second",
Expected: "string",
Found: args[1].TypeName(),
}
default:
return nil, ErrInvalidArgumentType{
Name: "first",
Expected: "map",
Found: arg.TypeName(),
}
}
}
// builtinSplice deletes and changes given Array, returns deleted items.
// usage:
// deleted_items := splice(array[,start[,delete_count[,item1[,item2[,...]]]])
func builtinSplice(args ...Object) (Object, error) {
argsLen := len(args)
if argsLen == 0 {
return nil, ErrWrongNumArguments
}
array, ok := args[0].(*Array)
if !ok {
return nil, ErrInvalidArgumentType{
Name: "first",
Expected: "array",
Found: args[0].TypeName(),
}
}
arrayLen := len(array.Value)
var startIdx int
if argsLen > 1 {
arg1, ok := args[1].(*Int)
if !ok {
return nil, ErrInvalidArgumentType{
Name: "second",
Expected: "int",
Found: args[1].TypeName(),
}
}
startIdx = int(arg1.Value)
if startIdx < 0 || startIdx > arrayLen {
return nil, ErrIndexOutOfBounds
}
}
delCount := len(array.Value)
if argsLen > 2 {
arg2, ok := args[2].(*Int)
if !ok {
return nil, ErrInvalidArgumentType{
Name: "third",
Expected: "int",
Found: args[2].TypeName(),
}
}
delCount = int(arg2.Value)
if delCount < 0 {
return nil, ErrIndexOutOfBounds
}
}
// if count of to be deleted items is bigger than expected, truncate it
if startIdx+delCount > arrayLen {
delCount = arrayLen - startIdx
}
// delete items
endIdx := startIdx + delCount
deleted := append([]Object{}, array.Value[startIdx:endIdx]...)
head := array.Value[:startIdx]
var items []Object
if argsLen > 3 {
items = make([]Object, 0, argsLen-3)
for i := 3; i < argsLen; i++ {
items = append(items, args[i])
}
}
items = append(items, array.Value[endIdx:]...)
array.Value = append(head, items...)
// return deleted items
return &Array{Value: deleted}, nil
}

View File

@@ -97,6 +97,7 @@ func (b *Bytecode) RemoveDuplicates() {
var deduped []Object
indexMap := make(map[int]int) // mapping from old constant index to new index
fns := make(map[*CompiledFunction]int)
ints := make(map[int64]int)
strings := make(map[string]int)
floats := make(map[float64]int)
@@ -106,9 +107,14 @@ func (b *Bytecode) RemoveDuplicates() {
for curIdx, c := range b.Constants {
switch c := c.(type) {
case *CompiledFunction:
// add to deduped list
indexMap[curIdx] = len(deduped)
deduped = append(deduped, c)
if newIdx, ok := fns[c]; ok {
indexMap[curIdx] = newIdx
} else {
newIdx = len(deduped)
fns[c] = newIdx
indexMap[curIdx] = newIdx
deduped = append(deduped, c)
}
case *ImmutableMap:
modName := inferModuleName(c)
newIdx, ok := immutableMaps[modName]

View File

@@ -44,6 +44,7 @@ type Compiler struct {
file *parser.SourceFile
parent *Compiler
modulePath string
importDir string
constants []Object
symbolTable *SymbolTable
scopes []compilationScope
@@ -505,7 +506,11 @@ func (c *Compiler) Compile(node parser.Node) error {
return err
}
}
c.emit(node, parser.OpCall, len(node.Args))
ellipsis := 0
if node.Ellipsis.IsValid() {
ellipsis = 1
}
c.emit(node, parser.OpCall, len(node.Args), ellipsis)
case *parser.ImportExpr:
if node.ModuleName == "" {
return c.errorf(node, "empty module name")
@@ -520,12 +525,12 @@ func (c *Compiler) Compile(node parser.Node) error {
switch v := v.(type) {
case []byte: // module written in Tengo
compiled, err := c.compileModule(node,
node.ModuleName, node.ModuleName, v)
node.ModuleName, v, false)
if err != nil {
return err
}
c.emit(node, parser.OpConstant, c.addConstant(compiled))
c.emit(node, parser.OpCall, 0)
c.emit(node, parser.OpCall, 0, 0)
case Object: // builtin module
c.emit(node, parser.OpConstant, c.addConstant(v))
default:
@@ -537,29 +542,25 @@ func (c *Compiler) Compile(node parser.Node) error {
moduleName += ".tengo"
}
modulePath, err := filepath.Abs(moduleName)
modulePath, err := filepath.Abs(
filepath.Join(c.importDir, moduleName))
if err != nil {
return c.errorf(node, "module file path error: %s",
err.Error())
}
if err := c.checkCyclicImports(node, modulePath); err != nil {
return err
}
moduleSrc, err := ioutil.ReadFile(moduleName)
moduleSrc, err := ioutil.ReadFile(modulePath)
if err != nil {
return c.errorf(node, "module file read error: %s",
err.Error())
}
compiled, err := c.compileModule(node,
moduleName, modulePath, moduleSrc)
compiled, err := c.compileModule(node, modulePath, moduleSrc, true)
if err != nil {
return err
}
c.emit(node, parser.OpConstant, c.addConstant(compiled))
c.emit(node, parser.OpCall, 0)
c.emit(node, parser.OpCall, 0, 0)
} else {
return c.errorf(node, "module '%s' not found", node.ModuleName)
}
@@ -634,6 +635,11 @@ func (c *Compiler) EnableFileImport(enable bool) {
c.allowFileImport = enable
}
// SetImportDir sets the initial import directory path for file imports.
func (c *Compiler) SetImportDir(dir string) {
c.importDir = dir
}
func (c *Compiler) compileAssign(
node parser.Node,
lhs, rhs []parser.Expr,
@@ -847,8 +853,8 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
// ... body ...
// }
//
// ":it" is a local variable but will be conflict with other user variables
// because character ":" is not allowed.
// ":it" is a local variable but it will not conflict with other user variables
// because character ":" is not allowed in the variable names.
// init
// :it = iterator(iterable)
@@ -893,6 +899,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
if keySymbol.Scope == ScopeGlobal {
c.emit(stmt, parser.OpSetGlobal, keySymbol.Index)
} else {
keySymbol.LocalAssigned = true
c.emit(stmt, parser.OpDefineLocal, keySymbol.Index)
}
}
@@ -909,6 +916,7 @@ func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error {
if valueSymbol.Scope == ScopeGlobal {
c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index)
} else {
valueSymbol.LocalAssigned = true
c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index)
}
}
@@ -955,8 +963,9 @@ func (c *Compiler) checkCyclicImports(
func (c *Compiler) compileModule(
node parser.Node,
moduleName, modulePath string,
modulePath string,
src []byte,
isFile bool,
) (*CompiledFunction, error) {
if err := c.checkCyclicImports(node, modulePath); err != nil {
return nil, err
@@ -967,7 +976,7 @@ func (c *Compiler) compileModule(
return compiledModule, nil
}
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
modFile := c.file.Set().AddFile(modulePath, -1, len(src))
p := parser.NewParser(modFile, src, nil)
file, err := p.ParseFile()
if err != nil {
@@ -984,7 +993,7 @@ func (c *Compiler) compileModule(
symbolTable = symbolTable.Fork(false)
// compile module
moduleCompiler := c.fork(modFile, modulePath, symbolTable)
moduleCompiler := c.fork(modFile, modulePath, symbolTable, isFile)
if err := moduleCompiler.Compile(file); err != nil {
return nil, err
}
@@ -1082,10 +1091,16 @@ func (c *Compiler) fork(
file *parser.SourceFile,
modulePath string,
symbolTable *SymbolTable,
isFile bool,
) *Compiler {
child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
child.modulePath = modulePath // module file path
child.parent = c // parent to set to current compiler
child.allowFileImport = c.allowFileImport
child.importDir = c.importDir
if isFile && c.importDir != "" {
child.importDir = filepath.Dir(modulePath)
}
return child
}
@@ -1192,6 +1207,7 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
var lastOp parser.Opcode
var appendReturn bool
endPos := len(c.scopes[c.scopeIndex].Instructions)
newEndPost := len(newInsts)
iterateInstructions(newInsts,
func(pos int, opcode parser.Opcode, operands []int) bool {
switch opcode {
@@ -1204,6 +1220,8 @@ func (c *Compiler) optimizeFunc(node parser.Node) {
} else if endPos == operands[0] {
// there's a jump instruction that jumps to the end of
// function compiler should append "return".
copy(newInsts[pos:],
MakeInstruction(opcode, newEndPost))
appendReturn = true
} else {
panic(fmt.Errorf("invalid jump position: %d", newDst))

View File

@@ -1342,6 +1342,38 @@ func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
}
return &String{Value: o.Value + rhsStr}, nil
}
case token.Less:
switch rhs := rhs.(type) {
case *String:
if o.Value < rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
case token.LessEq:
switch rhs := rhs.(type) {
case *String:
if o.Value <= rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
case token.Greater:
switch rhs := rhs.(type) {
case *String:
if o.Value > rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
case token.GreaterEq:
switch rhs := rhs.(type) {
case *String:
if o.Value >= rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
}
return nil, ErrInvalidOperator
}

View File

@@ -111,10 +111,11 @@ func (e *BoolLit) String() string {
// CallExpr represents a function call expression.
type CallExpr struct {
Func Expr
LParen Pos
Args []Expr
RParen Pos
Func Expr
LParen Pos
Args []Expr
Ellipsis Pos
RParen Pos
}
func (e *CallExpr) exprNode() {}
@@ -134,6 +135,9 @@ func (e *CallExpr) String() string {
for _, e := range e.Args {
args = append(args, e.String())
}
if len(args) > 0 && e.Ellipsis.IsValid() {
args[len(args)-1] = args[len(args)-1] + "..."
}
return e.Func.String() + "(" + strings.Join(args, ", ") + ")"
}

View File

@@ -120,7 +120,7 @@ var OpcodeOperands = [...][]int{
OpImmutable: {},
OpIndex: {},
OpSliceIndex: {},
OpCall: {1},
OpCall: {1, 1},
OpReturn: {1},
OpGetLocal: {1},
OpSetLocal: {1},

View File

@@ -270,9 +270,13 @@ func (p *Parser) parseCall(x Expr) *CallExpr {
p.exprLevel++
var list []Expr
for p.token != token.RParen && p.token != token.EOF {
var ellipsis Pos
for p.token != token.RParen && p.token != token.EOF && !ellipsis.IsValid() {
list = append(list, p.parseExpr())
if p.token == token.Ellipsis {
ellipsis = p.pos
p.next()
}
if !p.expectComma(token.RParen, "call argument") {
break
}
@@ -281,10 +285,11 @@ func (p *Parser) parseCall(x Expr) *CallExpr {
p.exprLevel--
rparen := p.expect(token.RParen)
return &CallExpr{
Func: x,
LParen: lparen,
RParen: rparen,
Args: list,
Func: x,
LParen: lparen,
RParen: rparen,
Ellipsis: ellipsis,
Args: list,
}
}

View File

@@ -3,6 +3,7 @@ package tengo
import (
"context"
"fmt"
"path/filepath"
"sync"
"github.com/d5/tengo/v2/parser"
@@ -16,6 +17,7 @@ type Script struct {
maxAllocs int64
maxConstObjects int
enableFileImport bool
importDir string
}
// NewScript creates a Script instance with an input script.
@@ -56,6 +58,16 @@ func (s *Script) SetImports(modules *ModuleMap) {
s.modules = modules
}
// SetImportDir sets the initial import directory for script files.
func (s *Script) SetImportDir(dir string) error {
dir, err := filepath.Abs(dir)
if err != nil {
return err
}
s.importDir = dir
return nil
}
// SetMaxAllocs sets the maximum number of objects allocations during the run
// time. Compiled script will return ErrObjectAllocLimit error if it
// exceeds this limit.
@@ -93,6 +105,7 @@ func (s *Script) Compile() (*Compiled, error) {
c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
c.EnableFileImport(s.enableFileImport)
c.SetImportDir(s.importDir)
if err := c.Compile(file); err != nil {
return nil, err
}

View File

@@ -1,20 +1,129 @@
// A modified version of Go's JSON implementation.
// Copyright 2010 The Go Authors. All rights reserved.
// Copyright 2010, 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json
import (
"bytes"
"encoding/base64"
"errors"
"math"
"strconv"
"unicode/utf8"
"github.com/d5/tengo/v2"
)
// safeSet holds the value true if the ASCII character with the given array
// position can be represented inside a JSON string without any further
// escaping.
//
// All values are true except for the ASCII control characters (0-31), the
// double quote ("), and the backslash character ("\").
var safeSet = [utf8.RuneSelf]bool{
' ': true,
'!': true,
'"': false,
'#': true,
'$': true,
'%': true,
'&': true,
'\'': true,
'(': true,
')': true,
'*': true,
'+': true,
',': true,
'-': true,
'.': true,
'/': true,
'0': true,
'1': true,
'2': true,
'3': true,
'4': true,
'5': true,
'6': true,
'7': true,
'8': true,
'9': true,
':': true,
';': true,
'<': true,
'=': true,
'>': true,
'?': true,
'@': true,
'A': true,
'B': true,
'C': true,
'D': true,
'E': true,
'F': true,
'G': true,
'H': true,
'I': true,
'J': true,
'K': true,
'L': true,
'M': true,
'N': true,
'O': true,
'P': true,
'Q': true,
'R': true,
'S': true,
'T': true,
'U': true,
'V': true,
'W': true,
'X': true,
'Y': true,
'Z': true,
'[': true,
'\\': false,
']': true,
'^': true,
'_': true,
'`': true,
'a': true,
'b': true,
'c': true,
'd': true,
'e': true,
'f': true,
'g': true,
'h': true,
'i': true,
'j': true,
'k': true,
'l': true,
'm': true,
'n': true,
'o': true,
'p': true,
'q': true,
'r': true,
's': true,
't': true,
'u': true,
'v': true,
'w': true,
'x': true,
'y': true,
'z': true,
'{': true,
'|': true,
'}': true,
'~': true,
'\u007f': true,
}
var hex = "0123456789abcdef"
// Encode returns the JSON encoding of the object.
func Encode(o tengo.Object) ([]byte, error) {
var b []byte
@@ -53,7 +162,7 @@ func Encode(o tengo.Object) ([]byte, error) {
len1 := len(o.Value) - 1
idx := 0
for key, value := range o.Value {
b = strconv.AppendQuote(b, key)
b = encodeString(b, key)
b = append(b, ':')
eb, err := Encode(value)
if err != nil {
@@ -71,7 +180,7 @@ func Encode(o tengo.Object) ([]byte, error) {
len1 := len(o.Value) - 1
idx := 0
for key, value := range o.Value {
b = strconv.AppendQuote(b, key)
b = encodeString(b, key)
b = append(b, ':')
eb, err := Encode(value)
if err != nil {
@@ -130,7 +239,9 @@ func Encode(o tengo.Object) ([]byte, error) {
case *tengo.Int:
b = strconv.AppendInt(b, o.Value, 10)
case *tengo.String:
b = strconv.AppendQuote(b, o.Value)
// string encoding bug is fixed with newly introduced function
// encodeString(). See: https://github.com/d5/tengo/issues/268
b = encodeString(b, o.Value)
case *tengo.Time:
y, err := o.Value.MarshalJSON()
if err != nil {
@@ -144,3 +255,79 @@ func Encode(o tengo.Object) ([]byte, error) {
}
return b, nil
}
// encodeString encodes given string as JSON string according to
// https://www.json.org/img/string.png
// Implementation is inspired by https://github.com/json-iterator/go
// See encodeStringSlowPath() for more information.
func encodeString(b []byte, val string) []byte {
valLen := len(val)
buf := bytes.NewBuffer(b)
buf.WriteByte('"')
// write string, the fast path, without utf8 and escape support
i := 0
for ; i < valLen; i++ {
c := val[i]
if c > 31 && c != '"' && c != '\\' {
buf.WriteByte(c)
} else {
break
}
}
if i == valLen {
buf.WriteByte('"')
return buf.Bytes()
}
encodeStringSlowPath(buf, i, val, valLen)
buf.WriteByte('"')
return buf.Bytes()
}
// encodeStringSlowPath is ported from Go 1.14.2 encoding/json package.
// U+2028 U+2029 JSONP security holes can be fixed with addition call to
// json.html_escape() thus it is removed from the implementation below.
// Note: Invalid runes are not checked as they are checked in original
// implementation.
func encodeStringSlowPath(buf *bytes.Buffer, i int, val string, valLen int) {
start := i
for i < valLen {
if b := val[i]; b < utf8.RuneSelf {
if safeSet[b] {
i++
continue
}
if start < i {
buf.WriteString(val[start:i])
}
buf.WriteByte('\\')
switch b {
case '\\', '"':
buf.WriteByte(b)
case '\n':
buf.WriteByte('n')
case '\r':
buf.WriteByte('r')
case '\t':
buf.WriteByte('t')
default:
// This encodes bytes < 0x20 except for \t, \n and \r.
// If escapeHTML is set, it also escapes <, >, and &
// because they can lead to security holes when
// user-controlled strings are rendered into JSON
// and served to some browsers.
buf.WriteString(`u00`)
buf.WriteByte(hex[b>>4])
buf.WriteByte(hex[b&0xF])
}
i++
start = i
continue
}
i++
continue
}
if start < valLen {
buf.WriteString(val[start:])
}
}

32
vendor/github.com/d5/tengo/v2/vm.go generated vendored
View File

@@ -80,14 +80,14 @@ func (v *VM) Run() (err error) {
if err != nil {
filePos := v.fileSet.Position(
v.curFrame.fn.SourcePos(v.ip - 1))
err = fmt.Errorf("Runtime Error: %s\n\tat %s",
err.Error(), filePos)
err = fmt.Errorf("Runtime Error: %w\n\tat %s",
err, filePos)
for v.framesIndex > 1 {
v.framesIndex--
v.curFrame = &v.frames[v.framesIndex-1]
filePos = v.fileSet.Position(
v.curFrame.fn.SourcePos(v.curFrame.ip - 1))
err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
err = fmt.Errorf("%w\n\tat %s", err, filePos)
}
return err
}
@@ -537,12 +537,36 @@ func (v *VM) run() {
}
case parser.OpCall:
numArgs := int(v.curInsts[v.ip+1])
v.ip++
spread := int(v.curInsts[v.ip+2])
v.ip += 2
value := v.stack[v.sp-1-numArgs]
if !value.CanCall() {
v.err = fmt.Errorf("not callable: %s", value.TypeName())
return
}
if spread == 1 {
v.sp--
switch arr := v.stack[v.sp].(type) {
case *Array:
for _, item := range arr.Value {
v.stack[v.sp] = item
v.sp++
}
numArgs += len(arr.Value) - 1
case *ImmutableArray:
for _, item := range arr.Value {
v.stack[v.sp] = item
v.sp++
}
numArgs += len(arr.Value) - 1
default:
v.err = fmt.Errorf("not an array: %s", arr.TypeName())
return
}
}
if callee, ok := value.(*CompiledFunction); ok {
if callee.VarArgs {
// if the closure is variadic,

View File

@@ -1,5 +1,12 @@
root = true
[*]
[*.go]
indent_style = tab
indent_size = 4
insert_final_newline = true
[*.{yml,yaml}]
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

1
vendor/github.com/fsnotify/fsnotify/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1 @@
go.sum linguist-generated

View File

@@ -2,29 +2,35 @@ sudo: false
language: go
go:
- 1.8.x
- 1.9.x
- tip
- "stable"
- "1.11.x"
- "1.10.x"
- "1.9.x"
matrix:
include:
- go: "stable"
env: GOLINT=true
allow_failures:
- go: tip
fast_finish: true
before_script:
- go get -u github.com/golang/lint/golint
before_install:
- if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi
script:
- go test -v --race ./...
- go test --race ./...
after_script:
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
- test -z "$(golint ./... | tee /dev/stderr)"
- if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi
- go vet ./...
os:
- linux
- osx
- windows
notifications:
email: false

View File

@@ -1,5 +1,5 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Copyright (c) 2012 fsnotify Authors. All rights reserved.
Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are

View File

@@ -10,16 +10,16 @@ go get -u golang.org/x/sys/...
Cross platform: Windows, Linux, BSD and macOS.
|Adapter |OS |Status |
|----------|----------|----------|
|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)|
|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)|
|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
|fanotify |Linux 2.6.37+ | |
|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
| Adapter | OS | Status |
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
| kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
| ReadDirectoryChangesW | Windows | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) |
| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) |
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
\* Android and iOS are untested.
@@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
## Usage
```go
package main
import (
"log"
"github.com/fsnotify/fsnotify"
)
func main() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
err = watcher.Add("/tmp/foo")
if err != nil {
log.Fatal(err)
}
<-done
}
```
## Contributing
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
@@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created:
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
[#62]: https://github.com/howeyc/fsnotify/issues/62
[#18]: https://github.com/fsnotify/fsnotify/issues/18
[#11]: https://github.com/fsnotify/fsnotify/issues/11

View File

@@ -63,4 +63,6 @@ func (e Event) String() string {
}
// Common errors that can be reported by a watcher
var ErrEventOverflow = errors.New("fsnotify queue overflow")
var (
ErrEventOverflow = errors.New("fsnotify queue overflow")
)

5
vendor/github.com/fsnotify/fsnotify/go.mod generated vendored Normal file
View File

@@ -0,0 +1,5 @@
module github.com/fsnotify/fsnotify
go 1.13
require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9

2
vendor/github.com/fsnotify/fsnotify/go.sum generated vendored Normal file
View File

@@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) {
poller.fd = fd
// Create epoll fd
poller.epfd, errno = unix.EpollCreate1(0)
poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
if poller.epfd == -1 {
return nil, errno
}
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
if errno != nil {
return nil, errno
}

View File

@@ -8,4 +8,4 @@ package fsnotify
import "golang.org/x/sys/unix"
const openMode = unix.O_NONBLOCK | unix.O_RDONLY
const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC

View File

@@ -9,4 +9,4 @@ package fsnotify
import "golang.org/x/sys/unix"
// note: this constant is not defined on BSD
const openMode = unix.O_EVTONLY
const openMode = unix.O_EVTONLY | unix.O_CLOEXEC

View File

@@ -3,4 +3,6 @@ language: go
go:
- '1.10'
- '1.11'
- '1.12'
- '1.13'
- tip

View File

@@ -3,7 +3,7 @@
[![GoDoc](https://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api?status.svg)](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api)
[![Travis](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api.svg)](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api)
All methods are fairly self explanatory, and reading the godoc page should
All methods are fairly self explanatory, and reading the [godoc](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api) page should
explain everything. If something isn't clear, open an issue or submit
a pull request.
@@ -63,7 +63,7 @@ func main() {
```
There are more examples on the [wiki](https://github.com/go-telegram-bot-api/telegram-bot-api/wiki)
with detailed information on how to do many differen kinds of things.
with detailed information on how to do many different kinds of things.
It's a great place to get started on using keyboards, commands, or other
kinds of reply markup.

View File

@@ -19,34 +19,50 @@ import (
"github.com/technoweenie/multipartstreamer"
)
type HttpClient interface {
Do(req *http.Request) (*http.Response, error)
}
// BotAPI allows you to interact with the Telegram Bot API.
type BotAPI struct {
Token string `json:"token"`
Debug bool `json:"debug"`
Buffer int `json:"buffer"`
Self User `json:"-"`
Client *http.Client `json:"-"`
Self User `json:"-"`
Client HttpClient `json:"-"`
shutdownChannel chan interface{}
apiEndpoint string
}
// NewBotAPI creates a new BotAPI instance.
//
// It requires a token, provided by @BotFather on Telegram.
func NewBotAPI(token string) (*BotAPI, error) {
return NewBotAPIWithClient(token, &http.Client{})
return NewBotAPIWithClient(token, APIEndpoint, &http.Client{})
}
// NewBotAPIWithAPIEndpoint creates a new BotAPI instance
// and allows you to pass API endpoint.
//
// It requires a token, provided by @BotFather on Telegram and API endpoint.
func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) {
return NewBotAPIWithClient(token, apiEndpoint, &http.Client{})
}
// NewBotAPIWithClient creates a new BotAPI instance
// and allows you to pass a http.Client.
//
// It requires a token, provided by @BotFather on Telegram.
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
// It requires a token, provided by @BotFather on Telegram and API endpoint.
func NewBotAPIWithClient(token, apiEndpoint string, client HttpClient) (*BotAPI, error) {
bot := &BotAPI{
Token: token,
Client: client,
Buffer: 100,
Token: token,
Client: client,
Buffer: 100,
shutdownChannel: make(chan interface{}),
apiEndpoint: apiEndpoint,
}
self, err := bot.GetMe()
@@ -59,11 +75,21 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
return bot, nil
}
func (b *BotAPI) SetAPIEndpoint(apiEndpoint string) {
b.apiEndpoint = apiEndpoint
}
// MakeRequest makes a request to a specific endpoint with our token.
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
resp, err := bot.Client.PostForm(method, params)
req, err := http.NewRequest("POST", method, strings.NewReader(params.Encode()))
if err != nil {
return APIResponse{}, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := bot.Client.Do(req)
if err != nil {
return APIResponse{}, err
}
@@ -84,7 +110,7 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
if apiResp.Parameters != nil {
parameters = *apiResp.Parameters
}
return apiResp, Error{apiResp.Description, parameters}
return apiResp, &Error{Code: apiResp.ErrorCode, Message: apiResp.Description, ResponseParameters: parameters}
}
return apiResp, nil
@@ -186,7 +212,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna
return APIResponse{}, errors.New(ErrBadFileType)
}
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
req, err := http.NewRequest("POST", method, nil)
if err != nil {
@@ -430,7 +456,7 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
// RemoveWebhook unsets the webhook.
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
return bot.MakeRequest("setWebhook", url.Values{})
return bot.MakeRequest("deleteWebhook", url.Values{})
}
// SetWebhook sets a webhook.
@@ -487,10 +513,11 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
for {
select {
case <-bot.shutdownChannel:
close(ch)
return
default:
}
updates, err := bot.GetUpdates(config)
if err != nil {
log.Println(err)
@@ -966,3 +993,22 @@ func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, e
return bot.MakeRequest(config.method(), v)
}
// GetStickerSet get a sticker set.
func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
v, err := config.values()
if err != nil {
return StickerSet{}, err
}
bot.debugLog(config.method(), v, nil)
res, err := bot.MakeRequest(config.method(), v)
if err != nil {
return StickerSet{}, err
}
stickerSet := StickerSet{}
err = json.Unmarshal(res.Result, &stickerSet)
if err != nil {
return StickerSet{}, err
}
return stickerSet, nil
}

View File

@@ -1262,3 +1262,18 @@ func (config DeleteChatPhotoConfig) values() (url.Values, error) {
return v, nil
}
// GetStickerSetConfig contains information for get sticker set.
type GetStickerSetConfig struct {
Name string
}
func (config GetStickerSetConfig) method() string {
return "getStickerSet"
}
func (config GetStickerSetConfig) values() (url.Values, error) {
v := url.Values{}
v.Add("name", config.Name)
return v, nil
}

View File

@@ -0,0 +1,5 @@
module github.com/go-telegram-bot-api/telegram-bot-api
go 1.12
require github.com/technoweenie/multipartstreamer v1.0.1

View File

@@ -0,0 +1,2 @@
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=

View File

@@ -29,7 +29,8 @@ func NewDeleteMessage(chatID int64, messageID int) DeleteMessageConfig {
// NewMessageToChannel creates a new Message that is sent to a channel
// by username.
//
// username is the username of the channel, text is the message text.
// username is the username of the channel, text is the message text,
// and the username should be in the form of `@username`.
func NewMessageToChannel(username string, text string) MessageConfig {
return MessageConfig{
BaseChat: BaseChat{
@@ -604,6 +605,18 @@ func NewInlineQueryResultLocation(id, title string, latitude, longitude float64)
}
}
// NewInlineQueryResultVenue creates a new inline query venue.
func NewInlineQueryResultVenue(id, title, address string, latitude, longitude float64) InlineQueryResultVenue {
return InlineQueryResultVenue{
Type: "venue",
ID: id,
Title: title,
Address: address,
Latitude: latitude,
Longitude: longitude,
}
}
// NewEditMessageText allows you to edit the text of a message.
func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTextConfig {
return EditMessageTextConfig{
@@ -622,7 +635,7 @@ func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMess
ChatID: chatID,
MessageID: messageID,
},
Caption: caption,
Caption: caption,
}
}

View File

@@ -64,6 +64,9 @@ type User struct {
// It is normally a user's username, but falls back to a first/last
// name as available.
func (u *User) String() string {
if u == nil {
return ""
}
if u.UserName != "" {
return u.UserName
}
@@ -100,6 +103,7 @@ type Chat struct {
Photo *ChatPhoto `json:"photo"`
Description string `json:"description,omitempty"` // optional
InviteLink string `json:"invite_link,omitempty"` // optional
PinnedMessage *Message `json:"pinned_message"` // optional
}
// IsPrivate returns if the Chat is a private conversation.
@@ -142,6 +146,7 @@ type Message struct {
EditDate int `json:"edit_date"` // optional
Text string `json:"text"` // optional
Entities *[]MessageEntity `json:"entities"` // optional
CaptionEntities *[]MessageEntity `json:"caption_entities"` // optional
Audio *Audio `json:"audio"` // optional
Document *Document `json:"document"` // optional
Animation *ChatAnimation `json:"animation"` // optional
@@ -183,7 +188,7 @@ func (m *Message) IsCommand() bool {
}
entity := (*m.Entities)[0]
return entity.Offset == 0 && entity.Type == "bot_command"
return entity.Offset == 0 && entity.IsCommand()
}
// Command checks if the message was a command and if it was, returns the
@@ -249,12 +254,62 @@ type MessageEntity struct {
}
// ParseURL attempts to parse a URL contained within a MessageEntity.
func (entity MessageEntity) ParseURL() (*url.URL, error) {
if entity.URL == "" {
func (e MessageEntity) ParseURL() (*url.URL, error) {
if e.URL == "" {
return nil, errors.New(ErrBadURL)
}
return url.Parse(entity.URL)
return url.Parse(e.URL)
}
// IsMention returns true if the type of the message entity is "mention" (@username).
func (e MessageEntity) IsMention() bool {
return e.Type == "mention"
}
// IsHashtag returns true if the type of the message entity is "hashtag".
func (e MessageEntity) IsHashtag() bool {
return e.Type == "hashtag"
}
// IsCommand returns true if the type of the message entity is "bot_command".
func (e MessageEntity) IsCommand() bool {
return e.Type == "bot_command"
}
// IsUrl returns true if the type of the message entity is "url".
func (e MessageEntity) IsUrl() bool {
return e.Type == "url"
}
// IsEmail returns true if the type of the message entity is "email".
func (e MessageEntity) IsEmail() bool {
return e.Type == "email"
}
// IsBold returns true if the type of the message entity is "bold" (bold text).
func (e MessageEntity) IsBold() bool {
return e.Type == "bold"
}
// IsItalic returns true if the type of the message entity is "italic" (italic text).
func (e MessageEntity) IsItalic() bool {
return e.Type == "italic"
}
// IsCode returns true if the type of the message entity is "code" (monowidth string).
func (e MessageEntity) IsCode() bool {
return e.Type == "code"
}
// IsPre returns true if the type of the message entity is "pre" (monowidth block).
func (e MessageEntity) IsPre() bool {
return e.Type == "pre"
}
// IsTextLink returns true if the type of the message entity is "text_link" (clickable text URL).
func (e MessageEntity) IsTextLink() bool {
return e.Type == "text_link"
}
// PhotoSize contains information about photos.
@@ -286,13 +341,23 @@ type Document struct {
// Sticker contains information about a sticker.
type Sticker struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
Thumbnail *PhotoSize `json:"thumb"` // optional
Emoji string `json:"emoji"` // optional
FileSize int `json:"file_size"` // optional
SetName string `json:"set_name"` // optional
FileUniqueID string `json:"file_unique_id"`
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
Thumbnail *PhotoSize `json:"thumb"` // optional
Emoji string `json:"emoji"` // optional
FileSize int `json:"file_size"` // optional
SetName string `json:"set_name"` // optional
IsAnimated bool `json:"is_animated"` // optional
}
type StickerSet struct {
Name string `json:"name"`
Title string `json:"title"`
IsAnimated bool `json:"is_animated"`
ContainsMasks bool `json:"contains_masks"`
Stickers []Sticker `json:"stickers"`
}
// ChatAnimation contains information about an animation.
@@ -582,6 +647,7 @@ type InlineQueryResultPhoto struct {
Title string `json:"title"`
Description string `json:"description"`
Caption string `json:"caption"`
ParseMode string `json:"parse_mode"`
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
InputMessageContent interface{} `json:"input_message_content,omitempty"`
}
@@ -601,15 +667,15 @@ type InlineQueryResultCachedPhoto struct {
// InlineQueryResultGIF is an inline query response GIF.
type InlineQueryResultGIF struct {
Type string `json:"type"` // required
ID string `json:"id"` // required
URL string `json:"gif_url"` // required
Width int `json:"gif_width"`
Height int `json:"gif_height"`
Duration int `json:"gif_duration"`
ThumbURL string `json:"thumb_url"`
Title string `json:"title"`
Caption string `json:"caption"`
Type string `json:"type"` // required
ID string `json:"id"` // required
URL string `json:"gif_url"` // required
ThumbURL string `json:"thumb_url"` // required
Width int `json:"gif_width,omitempty"`
Height int `json:"gif_height,omitempty"`
Duration int `json:"gif_duration,omitempty"`
Title string `json:"title,omitempty"`
Caption string `json:"caption,omitempty"`
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
InputMessageContent interface{} `json:"input_message_content,omitempty"`
}
@@ -775,6 +841,23 @@ type InlineQueryResultLocation struct {
ThumbHeight int `json:"thumb_height"`
}
// InlineQueryResultVenue is an inline query response venue.
type InlineQueryResultVenue struct {
Type string `json:"type"` // required
ID string `json:"id"` // required
Latitude float64 `json:"latitude"` // required
Longitude float64 `json:"longitude"` // required
Title string `json:"title"` // required
Address string `json:"address"` // required
FoursquareID string `json:"foursquare_id"`
FoursquareType string `json:"foursquare_type"`
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
InputMessageContent interface{} `json:"input_message_content,omitempty"`
ThumbURL string `json:"thumb_url"`
ThumbWidth int `json:"thumb_width"`
ThumbHeight int `json:"thumb_height"`
}
// InlineQueryResultGame is an inline query response game.
type InlineQueryResultGame struct {
Type string `json:"type"`
@@ -897,6 +980,7 @@ type PreCheckoutQuery struct {
// Error is an error containing extra information returned by the Telegram API.
type Error struct {
Code int
Message string
ResponseParameters
}

View File

@@ -38,7 +38,6 @@ package proto
import (
"fmt"
"log"
"os"
"reflect"
"sort"
"strconv"
@@ -194,7 +193,7 @@ func (p *Properties) Parse(s string) {
// "bytes,49,opt,name=foo,def=hello!"
fields := strings.Split(s, ",") // breaks def=, but handled below.
if len(fields) < 2 {
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
log.Printf("proto: tag has too few fields: %q", s)
return
}
@@ -214,7 +213,7 @@ func (p *Properties) Parse(s string) {
p.WireType = WireBytes
// no numeric converter for non-numeric types
default:
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
log.Printf("proto: tag has unknown wire type: %q", s)
return
}

View File

@@ -28,6 +28,7 @@ const (
Safelink // Only link to trusted protocols
NofollowLinks // Only link with rel="nofollow"
NoreferrerLinks // Only link with rel="noreferrer"
NoopenerLinks // Only link with rel="noopener"
HrefTargetBlank // Add a blank target
CompletePage // Generate a complete HTML page
UseXHTML // Generate XHTML output instead of HTML
@@ -295,6 +296,9 @@ func appendLinkAttrs(attrs []string, flags Flags, link []byte) []string {
if flags&NoreferrerLinks != 0 {
val = append(val, "noreferrer")
}
if flags&NoopenerLinks != 0 {
val = append(val, "noopener")
}
if flags&HrefTargetBlank != 0 {
attrs = append(attrs, `target="_blank"`)
}
@@ -1110,7 +1114,9 @@ func (r *Renderer) writeTOC(w io.Writer, doc ast.Node) {
buf.WriteString("</a>")
return ast.GoToNext
}
nodeData.HeadingID = fmt.Sprintf("toc_%d", headingCount)
if nodeData.HeadingID == "" {
nodeData.HeadingID = fmt.Sprintf("toc_%d", headingCount)
}
if nodeData.Level == tocLevel {
buf.WriteString("</li>\n\n<li>")
} else if nodeData.Level < tocLevel {
@@ -1126,7 +1132,7 @@ func (r *Renderer) writeTOC(w io.Writer, doc ast.Node) {
}
}
fmt.Fprintf(&buf, `<a href="#toc_%d">`, headingCount)
fmt.Fprintf(&buf, `<a href="#%s">`, nodeData.HeadingID)
headingCount++
return ast.GoToNext
}

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