forked from lug/matterbridge
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f06e9b5605 | ||
|
|
7a3bb0e55c | ||
|
|
6e8f535e8b | ||
|
|
5619a75b05 | ||
|
|
53dfb78215 | ||
|
|
8e97cbab1e | ||
|
|
ce7b749fd5 | ||
|
|
6617bd6609 | ||
|
|
e610fb3201 | ||
|
|
40f1d35415 | ||
|
|
b79bf7d414 | ||
|
|
3724cc3a15 | ||
|
|
3418e8c9af | ||
|
|
9619dff334 | ||
|
|
1b2feb19e5 | ||
|
|
1829dc3d9f | ||
|
|
bd0e81f5a0 | ||
|
|
f04d360ee2 | ||
|
|
92f27281fa | ||
|
|
65781b9316 | ||
|
|
9be0be0316 | ||
|
|
9f5f004725 | ||
|
|
fed77cccf3 | ||
|
|
9b520dfb78 | ||
|
|
8ad2be10b2 | ||
|
|
2d277a15f5 | ||
|
|
d60468bb05 | ||
|
|
82d6210464 | ||
|
|
ff198042d2 | ||
|
|
6b47e29583 | ||
|
|
380c38674c | ||
|
|
3c14a0891e | ||
|
|
8513a07416 | ||
|
|
220485a849 | ||
|
|
4db34b0506 | ||
|
|
5677c912a8 | ||
|
|
7a24de15e4 | ||
|
|
99d9ea283a | ||
|
|
dac92a0e0a | ||
|
|
a25efb16f3 | ||
|
|
e4d73b29a1 | ||
|
|
8a875f292e | ||
|
|
60a85621ea | ||
|
|
115d20373c | ||
|
|
cdf33e5748 | ||
|
|
01d0a9f412 | ||
|
|
8cc2d3b4fe | ||
|
|
aba9e4f3be | ||
|
|
4d575ba13a | ||
|
|
7f0e4ad448 | ||
|
|
17cc14a9d2 | ||
|
|
1f8016182c | ||
|
|
caf9ef2c4b | ||
|
|
64b57f2da3 | ||
|
|
efd2c99862 | ||
|
|
cc05ba8907 | ||
|
|
16763b715a | ||
|
|
ffaa598796 |
@@ -7,7 +7,7 @@ run:
|
|||||||
# concurrency: 4
|
# concurrency: 4
|
||||||
|
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
deadline: 1m
|
deadline: 2m
|
||||||
|
|
||||||
# exit code when at least one issue was found, default is 1
|
# exit code when at least one issue was found, default is 1
|
||||||
issues-exit-code: 1
|
issues-exit-code: 1
|
||||||
@@ -105,10 +105,6 @@ linters-settings:
|
|||||||
# with golangci-lint call it on a directory with the changed file.
|
# with golangci-lint call it on a directory with the changed file.
|
||||||
check-exported: false
|
check-exported: false
|
||||||
unparam:
|
unparam:
|
||||||
# call graph construction algorithm (cha, rta). In general, use cha for libraries,
|
|
||||||
# and rta for programs with main packages. Default is cha.
|
|
||||||
algo: rta
|
|
||||||
|
|
||||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||||
|
|||||||
89
.travis.yml
89
.travis.yml
@@ -1,75 +1,56 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
|
||||||
- 1.11.x
|
|
||||||
go_import_path: github.com/42wim/matterbridge
|
go_import_path: github.com/42wim/matterbridge
|
||||||
|
|
||||||
# we have everything vendored
|
# We have everything vendored so this helps TravisCI not run `go get ...`.
|
||||||
install: true
|
install: true
|
||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 200
|
depth: 200
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- GOOS=linux GOARCH=amd64
|
|
||||||
- GO111MODULE=on
|
|
||||||
- GOLANGCI_VERSION="v1.14.0"
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
# It's ok if our code fails on unstable development versions of Go.
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
# Don't wait for tip tests to finish. Mark the test run green if the
|
|
||||||
# tests pass on the stable versions of Go.
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
||||||
before_script:
|
|
||||||
# Get version info from tags.
|
|
||||||
- MY_VERSION="$(git describe --tags)"
|
|
||||||
# Retrieve the golangci-lint linter binary.
|
|
||||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION}
|
|
||||||
# Retrieve and prepare CodeClimate's test coverage reporter.
|
|
||||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
||||||
- chmod +x ./cc-test-reporter
|
|
||||||
- ./cc-test-reporter before-build
|
|
||||||
|
|
||||||
script:
|
|
||||||
# Ensure that the module files are being kept correctly and that vendored dependencies are up-to-date.
|
|
||||||
- go mod tidy
|
|
||||||
- go mod vendor
|
|
||||||
- git diff --exit-code --quiet || (echo "Please run 'go mod tidy' to clean up the 'go.mod' and 'go.sum' files."; false)
|
|
||||||
|
|
||||||
# Run the linter.
|
|
||||||
- golangci-lint run
|
|
||||||
|
|
||||||
# Run all the tests with the race detector and generate coverage.
|
|
||||||
- go test -v -race -coverprofile c.out ./...
|
|
||||||
|
|
||||||
# Run the build script to generate the necessary binaries and images.
|
|
||||||
- /bin/bash ci/bintray.sh
|
|
||||||
|
|
||||||
after_script:
|
|
||||||
# Upload test coverage to CodeClimate.
|
|
||||||
- ./cc-test-reporter after-build --exit-code ${TRAVIS_TEST_RESULT}
|
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- /.*/
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
- stage: lint
|
||||||
|
# Run linting in one Go environment only.
|
||||||
|
script: ./ci/lint.sh
|
||||||
|
go: 1.12.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
- GOLANGCI_VERSION="v1.16.0"
|
||||||
|
- stage: test
|
||||||
|
# Run tests in a combination of Go environments.
|
||||||
|
script: ./ci/test.sh
|
||||||
|
go: 1.11.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=off
|
||||||
|
- script: ./ci/test.sh
|
||||||
|
go: 1.11.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
- script: ./ci/test.sh
|
||||||
|
go: 1.12.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
- REPORT_COVERAGE=1
|
||||||
|
- BINDEPLOY=1
|
||||||
|
|
||||||
|
before_deploy: /bin/bash ci/bintray.sh
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
on:
|
|
||||||
all_branches: true
|
|
||||||
provider: bintray
|
|
||||||
on:
|
on:
|
||||||
all_branches: true
|
all_branches: true
|
||||||
|
condition: $BINDEPLOY = 1
|
||||||
|
provider: bintray
|
||||||
edge:
|
edge:
|
||||||
branch: v1.8.47
|
branch: v1.8.47
|
||||||
file: ci/deploy.json
|
file: ci/deploy.json
|
||||||
user: 42wim
|
user: 42wim
|
||||||
on:
|
|
||||||
all_branches: true
|
|
||||||
key:
|
key:
|
||||||
secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI="
|
secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI="
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -35,6 +35,12 @@
|
|||||||
|
|
||||||
**Note:** Matter<em>most</em> isn't required to run matter<em>bridge</em>.</sup></div>
|
**Note:** Matter<em>most</em> isn't required to run matter<em>bridge</em>.</sup></div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://www.digitalocean.com/">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
### Table of Contents
|
### Table of Contents
|
||||||
* [Features](https://github.com/42wim/matterbridge/wiki/Features)
|
* [Features](https://github.com/42wim/matterbridge/wiki/Features)
|
||||||
* [Natively supported](#natively-supported)
|
* [Natively supported](#natively-supported)
|
||||||
@@ -44,9 +50,10 @@
|
|||||||
* [Screenshots](https://github.com/42wim/matterbridge/wiki/)
|
* [Screenshots](https://github.com/42wim/matterbridge/wiki/)
|
||||||
* [Installing](#installing)
|
* [Installing](#installing)
|
||||||
* [Binaries](#binaries)
|
* [Binaries](#binaries)
|
||||||
* [Building](#building)
|
* [Building](#building)
|
||||||
* [Configuration](#configuration)
|
* [Configuration](#configuration)
|
||||||
* [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config)
|
* [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config)
|
||||||
|
* [Settings](#settings)
|
||||||
* [Examples](#examples)
|
* [Examples](#examples)
|
||||||
* [Running](#running)
|
* [Running](#running)
|
||||||
* [Docker](#docker)
|
* [Docker](#docker)
|
||||||
@@ -75,7 +82,6 @@
|
|||||||
* [Slack](https://slack.com)
|
* [Slack](https://slack.com)
|
||||||
* [Discord](https://discordapp.com)
|
* [Discord](https://discordapp.com)
|
||||||
* [Telegram](https://telegram.org)
|
* [Telegram](https://telegram.org)
|
||||||
* [Hipchat](https://www.hipchat.com)
|
|
||||||
* [Rocket.chat](https://rocket.chat)
|
* [Rocket.chat](https://rocket.chat)
|
||||||
* [Matrix](https://matrix.org)
|
* [Matrix](https://matrix.org)
|
||||||
* [Steam](https://store.steampowered.com/)
|
* [Steam](https://store.steampowered.com/)
|
||||||
@@ -91,7 +97,7 @@
|
|||||||
* [Discourse](https://github.com/DeclanHoare/matterbabble)
|
* [Discourse](https://github.com/DeclanHoare/matterbabble)
|
||||||
|
|
||||||
### API
|
### API
|
||||||
The API is very basic at the moment.
|
The API is basic at the moment.
|
||||||
More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/Api).
|
More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/Api).
|
||||||
|
|
||||||
Used by the projects below. Feel free to make a PR to add your project to this list.
|
Used by the projects below. Feel free to make a PR to add your project to this list.
|
||||||
@@ -113,7 +119,7 @@ Questions or want to test on your favorite platform? Join below:
|
|||||||
* [Slack][mb-slack]
|
* [Slack][mb-slack]
|
||||||
* [Mattermost][mb-mattermost]
|
* [Mattermost][mb-mattermost]
|
||||||
* [Rocket.Chat][mb-rocketchat]
|
* [Rocket.Chat][mb-rocketchat]
|
||||||
* [XMPP][mb-xmpp]
|
* [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
|
||||||
* [Twitch][mb-twitch]
|
* [Twitch][mb-twitch]
|
||||||
* [Zulip][mb-zulip]
|
* [Zulip][mb-zulip]
|
||||||
* [Telegram][mb-telegram]
|
* [Telegram][mb-telegram]
|
||||||
@@ -123,13 +129,13 @@ See https://github.com/42wim/matterbridge/wiki
|
|||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
### Binaries
|
### Binaries
|
||||||
* Latest stable release [v1.14.1](https://github.com/42wim/matterbridge/releases/latest)
|
* Latest stable release [v1.15.0](https://github.com/42wim/matterbridge/releases/latest)
|
||||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
||||||
|
|
||||||
### Packages
|
### Packages
|
||||||
* [Overview](https://repology.org/metapackage/matterbridge/versions)
|
* [Overview](https://repology.org/metapackage/matterbridge/versions)
|
||||||
|
|
||||||
### Building
|
## Building
|
||||||
Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
|
Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
|
||||||
|
|
||||||
After Go is setup, download matterbridge to your $GOPATH directory.
|
After Go is setup, download matterbridge to your $GOPATH directory.
|
||||||
@@ -150,6 +156,9 @@ matterbridge
|
|||||||
### Basic configuration
|
### Basic configuration
|
||||||
See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
All possible [settings](https://github.com/42wim/matterbridge/wiki/Settings) for each bridge.
|
||||||
|
|
||||||
### Advanced configuration
|
### Advanced configuration
|
||||||
* [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
|
* [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
|
||||||
|
|
||||||
@@ -264,7 +273,13 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
|
|||||||
* https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
|
* https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
[](https://www.digitalocean.com/) for sponsoring demo/testing droplets.
|
|
||||||
|
<p>This project is supported by:</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://www.digitalocean.com/">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
Matterbridge wouldn't exist without these libraries:
|
Matterbridge wouldn't exist without these libraries:
|
||||||
* discord - https://github.com/bwmarrin/discordgo
|
* discord - https://github.com/bwmarrin/discordgo
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ type Protocol struct {
|
|||||||
ReplaceMessages [][]string // all protocols
|
ReplaceMessages [][]string // all protocols
|
||||||
ReplaceNicks [][]string // all protocols
|
ReplaceNicks [][]string // all protocols
|
||||||
RemoteNickFormat string // all protocols
|
RemoteNickFormat string // all protocols
|
||||||
RunCommands []string // irc
|
RunCommands []string // IRC
|
||||||
Server string // IRC,mattermost,XMPP,discord
|
Server string // IRC,mattermost,XMPP,discord
|
||||||
ShowJoinPart bool // all protocols
|
ShowJoinPart bool // all protocols
|
||||||
ShowTopicChange bool // slack
|
ShowTopicChange bool // slack
|
||||||
@@ -141,6 +141,7 @@ type Protocol struct {
|
|||||||
UseFirstName bool // telegram
|
UseFirstName bool // telegram
|
||||||
UseUserName bool // discord
|
UseUserName bool // discord
|
||||||
UseInsecureURL bool // telegram
|
UseInsecureURL bool // telegram
|
||||||
|
VerboseJoinPart bool // IRC
|
||||||
WebhookBindAddress string // mattermost, slack
|
WebhookBindAddress string // mattermost, slack
|
||||||
WebhookURL string // mattermost, slack
|
WebhookURL string // mattermost, slack
|
||||||
}
|
}
|
||||||
@@ -166,6 +167,13 @@ type Gateway struct {
|
|||||||
InOut []Bridge
|
InOut []Bridge
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Tengo struct {
|
||||||
|
InMessage string
|
||||||
|
Message string
|
||||||
|
RemoteNickFormat string
|
||||||
|
OutMessage string
|
||||||
|
}
|
||||||
|
|
||||||
type SameChannelGateway struct {
|
type SameChannelGateway struct {
|
||||||
Name string
|
Name string
|
||||||
Enable bool
|
Enable bool
|
||||||
@@ -190,6 +198,7 @@ type BridgeValues struct {
|
|||||||
WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
|
WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
|
||||||
Zulip map[string]Protocol
|
Zulip map[string]Protocol
|
||||||
General Protocol
|
General Protocol
|
||||||
|
Tengo Tengo
|
||||||
Gateway []Gateway
|
Gateway []Gateway
|
||||||
SameChannelGateway []SameChannelGateway
|
SameChannelGateway []SameChannelGateway
|
||||||
}
|
}
|
||||||
@@ -245,12 +254,12 @@ func newConfigFromString(logger *logrus.Entry, input []byte) *config {
|
|||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil {
|
if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil {
|
||||||
logger.Fatalf("Failed to parse the configuration: %#v", err)
|
logger.Fatalf("Failed to parse the configuration: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &BridgeValues{}
|
cfg := &BridgeValues{}
|
||||||
if err := viper.Unmarshal(cfg); err != nil {
|
if err := viper.Unmarshal(cfg); err != nil {
|
||||||
logger.Fatalf("Failed to load the configuration: %#v", err)
|
logger.Fatalf("Failed to load the configuration: %s", err)
|
||||||
}
|
}
|
||||||
return &config{
|
return &config{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if using webhooks, do not relay if it's ours
|
// if using webhooks, do not relay if it's ours
|
||||||
if b.useWebhook() && m.Author.Bot { // && b.isWebhookID(m.Author.ID) {
|
if b.useWebhook() && m.Author.Bot && b.isWebhookID(m.Author.ID) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -91,12 +90,13 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
|
|||||||
if b.GetBool("nosendjoinpart") {
|
if b.GetBool("nosendjoinpart") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
|
||||||
// QUIT isn't channel bound, happens for all channels on the bridge
|
|
||||||
if event.Command == "QUIT" {
|
|
||||||
channel = ""
|
|
||||||
}
|
|
||||||
msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||||
|
if b.GetBool("verbosejoinpart") {
|
||||||
|
b.Log.Debugf("<= Sending verbose JOIN_LEAVE event from %s to gateway", b.Account)
|
||||||
|
msg = config.Message{Username: "system", Text: event.Source.Name + " (" + event.Source.Ident + "@" + event.Source.Host + ") " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||||
|
} else {
|
||||||
|
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||||
|
}
|
||||||
b.Log.Debugf("<= Message is %#v", msg)
|
b.Log.Debugf("<= Message is %#v", msg)
|
||||||
b.Remote <- msg
|
b.Remote <- msg
|
||||||
return
|
return
|
||||||
@@ -160,7 +160,10 @@ func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) {
|
|||||||
b.handleNickServ()
|
b.handleNickServ()
|
||||||
b.handleRunCommands()
|
b.handleRunCommands()
|
||||||
// we are now fully connected
|
// we are now fully connected
|
||||||
b.connected <- nil
|
// only send on first connection
|
||||||
|
if b.FirstConnection {
|
||||||
|
b.connected <- nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
||||||
@@ -178,10 +181,6 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
|||||||
// strip action, we made an event if it was an action
|
// strip action, we made an event if it was an action
|
||||||
rmsg.Text += event.StripAction()
|
rmsg.Text += event.StripAction()
|
||||||
|
|
||||||
// strip IRC colors
|
|
||||||
re := regexp.MustCompile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
|
|
||||||
rmsg.Text = re.ReplaceAllString(rmsg.Text, "")
|
|
||||||
|
|
||||||
// start detecting the charset
|
// start detecting the charset
|
||||||
mycharset := b.GetString("Charset")
|
mycharset := b.GetString("Charset")
|
||||||
if mycharset == "" {
|
if mycharset == "" {
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ func (b *Birc) Send(msg config.Message) (string, error) {
|
|||||||
// we can be in between reconnects #385
|
// we can be in between reconnects #385
|
||||||
if !b.i.IsConnected() {
|
if !b.i.IsConnected() {
|
||||||
b.Log.Error("Not connected to server, dropping message")
|
b.Log.Error("Not connected to server, dropping message")
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a command
|
// Execute a command
|
||||||
@@ -231,7 +232,7 @@ func (b *Birc) getClient() (*girc.Client, error) {
|
|||||||
// fix strict user handling of girc
|
// fix strict user handling of girc
|
||||||
user := b.GetString("Nick")
|
user := b.GetString("Nick")
|
||||||
for !girc.IsValidUser(user) {
|
for !girc.IsValidUser(user) {
|
||||||
if len(user) == 1 {
|
if len(user) == 1 || len(user) == 0 {
|
||||||
user = "matterbridge"
|
user = "matterbridge"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,6 +186,12 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore non-post messages
|
||||||
|
if message.Post == nil {
|
||||||
|
b.Log.Debugf("ignoring nil message.Post: %#v", message)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore messages sent from matterbridge
|
// Ignore messages sent from matterbridge
|
||||||
if message.Post.Props != nil {
|
if message.Post.Props != nil {
|
||||||
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {
|
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {
|
||||||
|
|||||||
@@ -121,6 +121,12 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
|||||||
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle prefix hint for unthreaded messages.
|
||||||
|
if msg.ParentID == "msg-parent-not-found" {
|
||||||
|
msg.ParentID = ""
|
||||||
|
msg.Text = fmt.Sprintf("[thread]: %s", msg.Text)
|
||||||
|
}
|
||||||
|
|
||||||
// Upload a file if it exists
|
// Upload a file if it exists
|
||||||
if msg.Extra != nil {
|
if msg.Extra != nil {
|
||||||
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func (b *Brocketchat) getChannelID(name string) string {
|
|||||||
b.RLock()
|
b.RLock()
|
||||||
defer b.RUnlock()
|
defer b.RUnlock()
|
||||||
for k, v := range b.channelMap {
|
for k, v := range b.channelMap {
|
||||||
if v == name {
|
if v == name || v == "#"+name {
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,11 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
|
|||||||
msg.Channel = strings.TrimPrefix(msg.Channel, "#")
|
msg.Channel = strings.TrimPrefix(msg.Channel, "#")
|
||||||
channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel}
|
channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel}
|
||||||
|
|
||||||
|
// Make a action /me of the message
|
||||||
|
if msg.Event == config.EventUserAction {
|
||||||
|
msg.Text = "_" + msg.Text + "_"
|
||||||
|
}
|
||||||
|
|
||||||
// Delete message
|
// Delete message
|
||||||
if msg.Event == config.EventMsgDelete {
|
if msg.Event == config.EventMsgDelete {
|
||||||
if msg.ID == "" {
|
if msg.ID == "" {
|
||||||
|
|||||||
@@ -22,20 +22,20 @@ func (b *Bslack) handleSlack() {
|
|||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
b.Log.Debug("Start listening for Slack messages")
|
b.Log.Debug("Start listening for Slack messages")
|
||||||
for message := range messages {
|
for message := range messages {
|
||||||
if message.Event != config.EventUserTyping {
|
// don't do any action on deleted/typing messages
|
||||||
|
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete {
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
|
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||||
|
// cleanup the message
|
||||||
|
message.Text = b.replaceMention(message.Text)
|
||||||
|
message.Text = b.replaceVariable(message.Text)
|
||||||
|
message.Text = b.replaceChannel(message.Text)
|
||||||
|
message.Text = b.replaceURL(message.Text)
|
||||||
|
message.Text = html.UnescapeString(message.Text)
|
||||||
|
|
||||||
|
// Add the avatar
|
||||||
|
message.Avatar = b.users.getAvatar(message.UserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup the message
|
|
||||||
message.Text = b.replaceMention(message.Text)
|
|
||||||
message.Text = b.replaceVariable(message.Text)
|
|
||||||
message.Text = b.replaceChannel(message.Text)
|
|
||||||
message.Text = b.replaceURL(message.Text)
|
|
||||||
message.Text = html.UnescapeString(message.Text)
|
|
||||||
|
|
||||||
// Add the avatar
|
|
||||||
message.Avatar = b.users.getAvatar(message.UserID)
|
|
||||||
|
|
||||||
b.Log.Debugf("<= Message is %#v", message)
|
b.Log.Debugf("<= Message is %#v", message)
|
||||||
b.Remote <- *message
|
b.Remote <- *message
|
||||||
}
|
}
|
||||||
@@ -130,12 +130,18 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// It seems ev.SubMessage.Edited == nil when slack unfurls.
|
if ev.SubMessage != nil {
|
||||||
// Do not forward these messages. See Github issue #266.
|
// It seems ev.SubMessage.Edited == nil when slack unfurls.
|
||||||
if ev.SubMessage != nil &&
|
// Do not forward these messages. See Github issue #266.
|
||||||
ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
|
if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
|
||||||
ev.SubMessage.Edited == nil {
|
ev.SubMessage.Edited == nil {
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
|
// see hidden subtypes at https://api.slack.com/events/message
|
||||||
|
// these messages are sent when we add a message to a thread #709
|
||||||
|
if ev.SubType == "message_replied" && ev.Hidden {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ev.Files) > 0 {
|
if len(ev.Files) > 0 {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ type BLegacy struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewLegacy(cfg *bridge.Config) bridge.Bridger {
|
func NewLegacy(cfg *bridge.Config) bridge.Bridger {
|
||||||
return &BLegacy{Bslack: newBridge(cfg)}
|
b := &BLegacy{Bslack: newBridge(cfg)}
|
||||||
|
b.legacy = true
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BLegacy) Connect() error {
|
func (b *BLegacy) Connect() error {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type Bslack struct {
|
|||||||
|
|
||||||
channels *channels
|
channels *channels
|
||||||
users *users
|
users *users
|
||||||
|
legacy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -151,6 +152,18 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try to join a channel when in legacy
|
||||||
|
if b.legacy {
|
||||||
|
_, err := b.sc.JoinChannel(channel.Name)
|
||||||
|
if err != nil {
|
||||||
|
switch err.Error() {
|
||||||
|
case "name_taken", "restricted_action":
|
||||||
|
case "default":
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b.channels.populateChannels(false)
|
b.channels.populateChannels(false)
|
||||||
|
|
||||||
channelInfo, err := b.channels.getChannel(channel.Name)
|
channelInfo, err := b.channels.getChannel(channel.Name)
|
||||||
@@ -163,7 +176,8 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
|
|||||||
channel.Name = channelInfo.Name
|
channel.Name = channelInfo.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if !channelInfo.IsMember {
|
// we can't join a channel unless we are using legacy tokens #651
|
||||||
|
if !channelInfo.IsMember && !b.legacy {
|
||||||
return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name)
|
return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ func (b *users) populateUser(userID string) {
|
|||||||
// in case the previous query failed for some reason.
|
// in case the previous query failed for some reason.
|
||||||
} else {
|
} else {
|
||||||
b.usersSyncPoints[userID] = make(chan struct{})
|
b.usersSyncPoints[userID] = make(chan struct{})
|
||||||
|
defer func() {
|
||||||
|
// Wake up any waiting goroutines and remove the synchronization point.
|
||||||
|
close(b.usersSyncPoints[userID])
|
||||||
|
delete(b.usersSyncPoints, userID)
|
||||||
|
}()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,10 +111,6 @@ func (b *users) populateUser(userID string) {
|
|||||||
|
|
||||||
// Register user information.
|
// Register user information.
|
||||||
b.users[userID] = user
|
b.users[userID] = user
|
||||||
|
|
||||||
// Wake up any waiting goroutines and remove the synchronization point.
|
|
||||||
close(b.usersSyncPoints[userID])
|
|
||||||
delete(b.usersSyncPoints, userID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *users) populateUsers(wait bool) {
|
func (b *users) populateUsers(wait bool) {
|
||||||
|
|||||||
@@ -125,6 +125,11 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
|||||||
// handle groups
|
// handle groups
|
||||||
message = b.handleGroups(&rmsg, message, update)
|
message = b.handleGroups(&rmsg, message, update)
|
||||||
|
|
||||||
|
if message == nil {
|
||||||
|
b.Log.Error("message is nil, this shouldn't happen.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// set the ID's from the channel or group message
|
// set the ID's from the channel or group message
|
||||||
rmsg.ID = strconv.Itoa(message.MessageID)
|
rmsg.ID = strconv.Itoa(message.MessageID)
|
||||||
rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10)
|
rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10)
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
"github.com/matterbridge/go-whatsapp"
|
|
||||||
|
|
||||||
whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -21,6 +18,10 @@ Check:
|
|||||||
|
|
||||||
// HandleError received from WhatsApp
|
// HandleError received from WhatsApp
|
||||||
func (b *Bwhatsapp) HandleError(err error) {
|
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") {
|
||||||
|
return
|
||||||
|
}
|
||||||
b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
|
b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
|
|||||||
|
|
||||||
// mentions comes as telephone numbers and we don't want to expose it to other bridges
|
// mentions comes as telephone numbers and we don't want to expose it to other bridges
|
||||||
// replace it with something more meaninful to others
|
// replace it with something more meaninful to others
|
||||||
mention := b.getSenderNotify(numberAndSuffix[0] + whatsappExt.NewUserSuffix)
|
mention := b.getSenderNotify(numberAndSuffix[0] + "@s.whatsapp.net")
|
||||||
if mention == "" {
|
if mention == "" {
|
||||||
mention = "someone"
|
mention = "someone"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,22 @@ package bwhatsapp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
|
qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
|
||||||
"github.com/matterbridge/go-whatsapp"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ProfilePicInfo struct {
|
||||||
|
URL string `json:"eurl"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
|
||||||
|
Status int16 `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
func qrFromTerminal(invert bool) chan string {
|
func qrFromTerminal(invert bool) chan string {
|
||||||
qr := make(chan string)
|
qr := make(chan string)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -82,3 +91,17 @@ func (b *Bwhatsapp) getSenderNotify(senderJid string) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) {
|
||||||
|
data, err := b.conn.GetProfilePicThumb(jid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get avatar: %v", err)
|
||||||
|
}
|
||||||
|
content := <-data
|
||||||
|
info := &ProfilePicInfo{}
|
||||||
|
err = json.Unmarshal([]byte(content), info)
|
||||||
|
if err != nil {
|
||||||
|
return info, fmt.Errorf("failed to unmarshal avatar info: %v", err)
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/Rhymen/go-whatsapp"
|
||||||
"github.com/matterbridge/go-whatsapp"
|
|
||||||
|
|
||||||
whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -29,10 +26,8 @@ type Bwhatsapp struct {
|
|||||||
*bridge.Config
|
*bridge.Config
|
||||||
|
|
||||||
// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21
|
// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21
|
||||||
session *whatsapp.Session
|
session *whatsapp.Session
|
||||||
conn *whatsapp.Conn
|
conn *whatsapp.Conn
|
||||||
// https://github.com/tulir/mautrix-whatsapp/blob/master/whatsapp-ext/whatsapp.go
|
|
||||||
connExt *whatsappExt.ExtendedConn
|
|
||||||
startedAt uint64
|
startedAt uint64
|
||||||
|
|
||||||
users map[string]whatsapp.Contact
|
users map[string]whatsapp.Contact
|
||||||
@@ -74,8 +69,6 @@ func (b *Bwhatsapp) Connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.conn = conn
|
b.conn = conn
|
||||||
b.connExt = whatsappExt.ExtendConn(b.conn)
|
|
||||||
// TODO do we want to use it? b.connExt.SetClientName("Matterbridge WhatsApp bridge", "mb-wa")
|
|
||||||
|
|
||||||
b.conn.AddHandler(b)
|
b.conn.AddHandler(b)
|
||||||
b.Log.Debugln("WhatsApp connection successful")
|
b.Log.Debugln("WhatsApp connection successful")
|
||||||
@@ -89,7 +82,7 @@ func (b *Bwhatsapp) Connect() error {
|
|||||||
b.Log.Debugln("Restoring WhatsApp session..")
|
b.Log.Debugln("Restoring WhatsApp session..")
|
||||||
|
|
||||||
// https://github.com/Rhymen/go-whatsapp#restore
|
// https://github.com/Rhymen/go-whatsapp#restore
|
||||||
session, err = b.conn.RestoreSession(session)
|
session, err = b.conn.RestoreWithSession(session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO return or continue to normal login?
|
// TODO return or continue to normal login?
|
||||||
// restore session connection timed out (I couldn't get over it without logging in again)
|
// restore session connection timed out (I couldn't get over it without logging in again)
|
||||||
@@ -130,7 +123,7 @@ func (b *Bwhatsapp) Connect() error {
|
|||||||
b.Log.Debug("Getting user avatars..")
|
b.Log.Debug("Getting user avatars..")
|
||||||
|
|
||||||
for jid := range b.users {
|
for jid := range b.users {
|
||||||
info, err := b.connExt.GetProfilePicThumb(jid)
|
info, err := b.GetProfilePicThumb(jid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
|
b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
|
||||||
|
|
||||||
@@ -294,7 +287,7 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes))
|
text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes))
|
||||||
|
|
||||||
err := b.conn.Send(text)
|
_, err := b.conn.Send(text)
|
||||||
|
|
||||||
return text.Info.Id, err
|
return text.Info.Id, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,50 +14,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Bxmpp struct {
|
type Bxmpp struct {
|
||||||
xc *xmpp.Client
|
|
||||||
xmppMap map[string]string
|
|
||||||
*bridge.Config
|
*bridge.Config
|
||||||
|
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
|
xc *xmpp.Client
|
||||||
|
xmppMap map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *bridge.Config) bridge.Bridger {
|
func New(cfg *bridge.Config) bridge.Bridger {
|
||||||
b := &Bxmpp{Config: cfg}
|
return &Bxmpp{
|
||||||
b.xmppMap = make(map[string]string)
|
Config: cfg,
|
||||||
return b
|
xmppMap: make(map[string]string),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bxmpp) Connect() error {
|
func (b *Bxmpp) Connect() error {
|
||||||
var err error
|
|
||||||
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
||||||
b.xc, err = b.createXMPP()
|
if err := b.createXMPP(); err != nil {
|
||||||
if err != nil {
|
|
||||||
b.Log.Debugf("%#v", err)
|
b.Log.Debugf("%#v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Log.Info("Connection succeeded")
|
b.Log.Info("Connection succeeded")
|
||||||
go func() {
|
go b.manageConnection()
|
||||||
initial := true
|
|
||||||
bf := &backoff.Backoff{
|
|
||||||
Min: time.Second,
|
|
||||||
Max: 5 * time.Minute,
|
|
||||||
Jitter: true,
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
if initial {
|
|
||||||
b.handleXMPP()
|
|
||||||
initial = false
|
|
||||||
}
|
|
||||||
d := bf.Duration()
|
|
||||||
b.Log.Infof("Disconnected. Reconnecting in %s", d)
|
|
||||||
time.Sleep(d)
|
|
||||||
b.xc, err = b.createXMPP()
|
|
||||||
if err == nil {
|
|
||||||
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EventRejoinChannels}
|
|
||||||
b.handleXMPP()
|
|
||||||
bf.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,34 +61,48 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
b.Log.Debugf("=> Receiving %#v", msg)
|
b.Log.Debugf("=> Receiving %#v", msg)
|
||||||
|
|
||||||
// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)
|
// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
|
||||||
if msg.Extra != nil {
|
if msg.Extra != nil {
|
||||||
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})
|
b.Log.Debugf("=> Sending attachement message %#v", rmsg)
|
||||||
|
if _, err := b.xc.Send(xmpp.Chat{
|
||||||
|
Type: "groupchat",
|
||||||
|
Remote: rmsg.Channel + "@" + b.GetString("Muc"),
|
||||||
|
Text: rmsg.Username + rmsg.Text,
|
||||||
|
}); err != nil {
|
||||||
|
b.Log.WithError(err).Error("Unable to send message with share URL.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(msg.Extra["file"]) > 0 {
|
if len(msg.Extra["file"]) > 0 {
|
||||||
return b.handleUploadFile(&msg)
|
return "", b.handleUploadFile(&msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var msgreplaceid string
|
var msgReplaceID string
|
||||||
msgid := xid.New().String()
|
msgID := xid.New().String()
|
||||||
if msg.ID != "" {
|
if msg.ID != "" {
|
||||||
msgid = msg.ID
|
msgID = msg.ID
|
||||||
msgreplaceid = msg.ID
|
msgReplaceID = msg.ID
|
||||||
}
|
}
|
||||||
// Post normal message
|
// Post normal message.
|
||||||
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text, ID: msgid, ReplaceID: msgreplaceid})
|
b.Log.Debugf("=> Sending message %#v", msg)
|
||||||
if err != nil {
|
if _, err := b.xc.Send(xmpp.Chat{
|
||||||
|
Type: "groupchat",
|
||||||
|
Remote: msg.Channel + "@" + b.GetString("Muc"),
|
||||||
|
Text: msg.Username + msg.Text,
|
||||||
|
ID: msgID,
|
||||||
|
ReplaceID: msgReplaceID,
|
||||||
|
}); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return msgid, nil
|
return msgID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
func (b *Bxmpp) createXMPP() error {
|
||||||
tc := new(tls.Config)
|
tc := &tls.Config{
|
||||||
tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify")
|
ServerName: strings.Split(b.GetString("Jid"), "@")[1],
|
||||||
tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]
|
InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
|
||||||
|
}
|
||||||
options := xmpp.Options{
|
options := xmpp.Options{
|
||||||
Host: b.GetString("Server"),
|
Host: b.GetString("Server"),
|
||||||
User: b.GetString("Jid"),
|
User: b.GetString("Jid"),
|
||||||
@@ -127,7 +120,51 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
|||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
b.xc, err = options.NewClient()
|
b.xc, err = options.NewClient()
|
||||||
return b.xc, err
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bxmpp) manageConnection() {
|
||||||
|
initial := true
|
||||||
|
bf := &backoff.Backoff{
|
||||||
|
Min: time.Second,
|
||||||
|
Max: 5 * time.Minute,
|
||||||
|
Jitter: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main connection loop. Each iteration corresponds to a successful
|
||||||
|
// connection attempt and the subsequent handling of the connection.
|
||||||
|
for {
|
||||||
|
if initial {
|
||||||
|
initial = false
|
||||||
|
} else {
|
||||||
|
b.Remote <- config.Message{
|
||||||
|
Username: "system",
|
||||||
|
Text: "rejoin",
|
||||||
|
Channel: "",
|
||||||
|
Account: b.Account,
|
||||||
|
Event: config.EventRejoinChannels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.handleXMPP(); err != nil {
|
||||||
|
b.Log.WithError(err).Error("Disconnected.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconnection loop using an exponential back-off strategy. We
|
||||||
|
// only break out of the loop if we have successfully reconnected.
|
||||||
|
for {
|
||||||
|
d := bf.Duration()
|
||||||
|
b.Log.Infof("Reconnecting in %s.", d)
|
||||||
|
time.Sleep(d)
|
||||||
|
|
||||||
|
b.Log.Infof("Reconnecting now.")
|
||||||
|
if err := b.createXMPP(); err == nil {
|
||||||
|
bf.Reset()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.Log.Warn("Failed to reconnect.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bxmpp) xmppKeepAlive() chan bool {
|
func (b *Bxmpp) xmppKeepAlive() chan bool {
|
||||||
@@ -139,8 +176,7 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
b.Log.Debugf("PING")
|
b.Log.Debugf("PING")
|
||||||
err := b.xc.PingC2S("", "")
|
if err := b.xc.PingC2S("", ""); err != nil {
|
||||||
if err != nil {
|
|
||||||
b.Log.Debugf("PING failed %#v", err)
|
b.Log.Debugf("PING failed %#v", err)
|
||||||
}
|
}
|
||||||
case <-done:
|
case <-done:
|
||||||
@@ -152,31 +188,35 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bxmpp) handleXMPP() error {
|
func (b *Bxmpp) handleXMPP() error {
|
||||||
var ok bool
|
|
||||||
var msgid string
|
|
||||||
b.startTime = time.Now()
|
b.startTime = time.Now()
|
||||||
|
|
||||||
done := b.xmppKeepAlive()
|
done := b.xmppKeepAlive()
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
m, err := b.xc.Recv()
|
m, err := b.xc.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := m.(type) {
|
switch v := m.(type) {
|
||||||
case xmpp.Chat:
|
case xmpp.Chat:
|
||||||
if v.Type == "groupchat" {
|
if v.Type == "groupchat" {
|
||||||
b.Log.Debugf("== Receiving %#v", v)
|
b.Log.Debugf("== Receiving %#v", v)
|
||||||
event := ""
|
|
||||||
// skip invalid messages
|
// Skip invalid messages.
|
||||||
if b.skipMessage(v) {
|
if b.skipMessage(v) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var event string
|
||||||
if strings.Contains(v.Text, "has set the subject to:") {
|
if strings.Contains(v.Text, "has set the subject to:") {
|
||||||
event = config.EventTopicChange
|
event = config.EventTopicChange
|
||||||
}
|
}
|
||||||
msgid = v.ID
|
|
||||||
|
msgID := v.ID
|
||||||
if v.ReplaceID != "" {
|
if v.ReplaceID != "" {
|
||||||
msgid = v.ReplaceID
|
msgID = v.ReplaceID
|
||||||
}
|
}
|
||||||
rmsg := config.Message{
|
rmsg := config.Message{
|
||||||
Username: b.parseNick(v.Remote),
|
Username: b.parseNick(v.Remote),
|
||||||
@@ -184,21 +224,23 @@ func (b *Bxmpp) handleXMPP() error {
|
|||||||
Channel: b.parseChannel(v.Remote),
|
Channel: b.parseChannel(v.Remote),
|
||||||
Account: b.Account,
|
Account: b.Account,
|
||||||
UserID: v.Remote,
|
UserID: v.Remote,
|
||||||
ID: msgid,
|
ID: msgID,
|
||||||
Event: event,
|
Event: event,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we have an action event
|
// Check if we have an action event.
|
||||||
|
var ok bool
|
||||||
rmsg.Text, ok = b.replaceAction(rmsg.Text)
|
rmsg.Text, ok = b.replaceAction(rmsg.Text)
|
||||||
if ok {
|
if ok {
|
||||||
rmsg.Event = config.EventUserAction
|
rmsg.Event = config.EventUserAction
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
|
b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
|
||||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
b.Log.Debugf("<= Message is %#v", rmsg)
|
||||||
b.Remote <- rmsg
|
b.Remote <- rmsg
|
||||||
}
|
}
|
||||||
case xmpp.Presence:
|
case xmpp.Presence:
|
||||||
// do nothing
|
// Do nothing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,30 +253,41 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleUploadFile handles native upload of files
|
// handleUploadFile handles native upload of files
|
||||||
func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
|
func (b *Bxmpp) handleUploadFile(msg *config.Message) error {
|
||||||
var urldesc = ""
|
var urlDesc string
|
||||||
|
|
||||||
for _, f := range msg.Extra["file"] {
|
for _, file := range msg.Extra["file"] {
|
||||||
fi := f.(config.FileInfo)
|
fileInfo := file.(config.FileInfo)
|
||||||
if fi.Comment != "" {
|
if fileInfo.Comment != "" {
|
||||||
msg.Text += fi.Comment + ": "
|
msg.Text += fileInfo.Comment + ": "
|
||||||
}
|
}
|
||||||
if fi.URL != "" {
|
if fileInfo.URL != "" {
|
||||||
msg.Text = fi.URL
|
msg.Text = fileInfo.URL
|
||||||
if fi.Comment != "" {
|
if fileInfo.Comment != "" {
|
||||||
msg.Text = fi.Comment + ": " + fi.URL
|
msg.Text = fileInfo.Comment + ": " + fileInfo.URL
|
||||||
urldesc = fi.Comment
|
urlDesc = fileInfo.Comment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
|
if _, err := b.xc.Send(xmpp.Chat{
|
||||||
if err != nil {
|
Type: "groupchat",
|
||||||
return "", err
|
Remote: msg.Channel + "@" + b.GetString("Muc"),
|
||||||
|
Text: msg.Username + msg.Text,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if fi.URL != "" {
|
|
||||||
b.xc.SendOOB(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Ooburl: fi.URL, Oobdesc: urldesc})
|
if fileInfo.URL != "" {
|
||||||
|
if _, err := b.xc.SendOOB(xmpp.Chat{
|
||||||
|
Type: "groupchat",
|
||||||
|
Remote: msg.Channel + "@" + b.GetString("Muc"),
|
||||||
|
Ooburl: fileInfo.URL,
|
||||||
|
Oobdesc: urlDesc,
|
||||||
|
}); err != nil {
|
||||||
|
b.Log.WithError(err).Warn("Failed to send share URL.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bxmpp) parseNick(remote string) string {
|
func (b *Bxmpp) parseNick(remote string) string {
|
||||||
@@ -279,6 +332,5 @@ func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skip delayed messages
|
// skip delayed messages
|
||||||
t := time.Time{}
|
return !message.Stamp.IsZero() && time.Since(message.Stamp).Minutes() > 5
|
||||||
return message.Stamp != t
|
|
||||||
}
|
}
|
||||||
|
|||||||
58
changelog.md
58
changelog.md
@@ -1,3 +1,61 @@
|
|||||||
|
# v1.15.0
|
||||||
|
## New features
|
||||||
|
* Add scripting (tengo) support for every outgoing message (#806)
|
||||||
|
See https://github.com/42wim/matterbridge/wiki/Settings#tengo and
|
||||||
|
https://github.com/42wim/matterbridge/wiki/Settings#outmessage for more information
|
||||||
|
* Add tengo support to RemoteNickFormat (#793)
|
||||||
|
See https://github.com/42wim/matterbridge/wiki/Settings#remotenickformat-2
|
||||||
|
* Deprecated `Message` under `[tengo]` to `InMessage`
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
* general: Forward only user-typing messages if supported by protocol (#832)
|
||||||
|
* general: updated wiki with all possible settings: https://github.com/42wim/matterbridge/wiki/Settings
|
||||||
|
* tengo: Add msg event to tengo
|
||||||
|
* xmpp: Verify TLS against JID domain, not the host. (xmpp) (#834)
|
||||||
|
* xmpp: Allow messages with timestamp (xmpp). Fixes #835 (#847)
|
||||||
|
* irc: Add verbose IRC joins/parts (ident@host) (#805)
|
||||||
|
See https://github.com/42wim/matterbridge/wiki/Settings#verbosejoinpart
|
||||||
|
* rocketchat: Add useraction support (rocketchat). Closes #772 (#794)
|
||||||
|
|
||||||
|
## Bugfix
|
||||||
|
* slack: Fix regression in autojoining with legacy tokens (slack). Fixes #651 (#848)
|
||||||
|
* xmpp: Revert xmpp to orig behaviour. Closes #844
|
||||||
|
* whatsapp: Update github.com/Rhymen/go-whatsapp vendor. Fixes #843
|
||||||
|
* mattermost: Update channels of all teams (mattermost)
|
||||||
|
|
||||||
|
This release couldn't exist without the following contributors:
|
||||||
|
@42wim, @Helcaraxan, @chotaire, @qaisjp, @dajohi, @kousu
|
||||||
|
|
||||||
|
# v1.14.4
|
||||||
|
|
||||||
|
## Bugfix
|
||||||
|
* mattermost: Add Id to EditMessage (mattermost). Fixes #802
|
||||||
|
* mattermost: Fix panic on nil message.Post (mattermost). Fixes #804
|
||||||
|
* mattermost: Handle unthreaded messages (mattermost). Fixes #803
|
||||||
|
* mattermost: Use paging in initUser and UpdateUsers (mattermost)
|
||||||
|
* slack: Add lacking clean-up in Slack synchronisation (#811)
|
||||||
|
* slack: Disable user lookups on delete messages (slack) (#812)
|
||||||
|
|
||||||
|
# v1.14.3
|
||||||
|
|
||||||
|
## Bugfix
|
||||||
|
* irc: Fix deadlock on reconnect (irc). Closes #757
|
||||||
|
|
||||||
|
# v1.14.2
|
||||||
|
|
||||||
|
## Bugfix
|
||||||
|
* general: Update tengo vendor and load the stdlib. Fixes #789 (#792)
|
||||||
|
* rocketchat: Look up #channel too (rocketchat). Fix #773 (#775)
|
||||||
|
* slack: Ignore messagereplied and hidden messages (slack). Fixes #709 (#779)
|
||||||
|
* telegram: Handle nil message (telegram). Fixes #777
|
||||||
|
* irc: Use default nick if none specified (irc). Fixes #785
|
||||||
|
* irc: Return when not connected and drop a message (irc). Fixes #786
|
||||||
|
* irc: Revert fix for #722 (Support quits from irc correctly). Closes #781
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
This release couldn't exist without the following contributors:
|
||||||
|
@42wim, @Helcaraxan, @dajohi
|
||||||
|
|
||||||
# v1.14.1
|
# v1.14.1
|
||||||
## Bugfix
|
## Bugfix
|
||||||
* slack: Fix crash double unlock (slack) (#771)
|
* slack: Fix crash double unlock (slack) (#771)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
go version | grep go1.11 || exit
|
set -u -e -x -o pipefail
|
||||||
|
|
||||||
|
go version | grep go1.12 || exit
|
||||||
|
|
||||||
VERSION=$(git describe --tags)
|
VERSION=$(git describe --tags)
|
||||||
mkdir ci/binaries
|
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=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe
|
||||||
|
|||||||
17
ci/lint.sh
Executable file
17
ci/lint.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -u -e -x -o pipefail
|
||||||
|
|
||||||
|
if [[ -n "${GOLANGCI_VERSION-}" ]]; then
|
||||||
|
# Retrieve the golangci-lint linter binary.
|
||||||
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the linter.
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
|
if [[ "${GO111MODULE-off}" == "on" ]]; then
|
||||||
|
# If Go modules are active then check that dependencies are correctly maintained.
|
||||||
|
go mod tidy
|
||||||
|
go mod vendor
|
||||||
|
git diff --exit-code --quiet || (echo "Please run 'go mod tidy' to clean up the 'go.mod' and 'go.sum' files."; false)
|
||||||
|
fi
|
||||||
17
ci/test.sh
Executable file
17
ci/test.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -u -e -x -o pipefail
|
||||||
|
|
||||||
|
if [[ -n "${REPORT_COVERAGE+cover}" ]]; then
|
||||||
|
# Retrieve and prepare CodeClimate's test coverage reporter.
|
||||||
|
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||||
|
chmod +x ./cc-test-reporter
|
||||||
|
./cc-test-reporter before-build
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run all the tests with the race detector and generate coverage.
|
||||||
|
go test -v -race -coverprofile c.out ./...
|
||||||
|
|
||||||
|
if [[ -n "${REPORT_COVERAGE+cover}" && "${TRAVIS_SECURE_ENV_VARS}" == "true" ]]; then
|
||||||
|
# Upload test coverage to CodeClimate.
|
||||||
|
./cc-test-reporter after-build
|
||||||
|
fi
|
||||||
7
contrib/outmessage-irccolors.tengo
Normal file
7
contrib/outmessage-irccolors.tengo
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// See https://github.com/42wim/matterbridge/issues/798
|
||||||
|
|
||||||
|
// if we're not sending to an irc bridge we strip the IRC colors
|
||||||
|
if outProtocol != "irc" {
|
||||||
|
re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
|
||||||
|
msgText=re.replace(msgText,"")
|
||||||
|
}
|
||||||
16
contrib/remotenickformat-zerowidth.tengo
Normal file
16
contrib/remotenickformat-zerowidth.tengo
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
This script will return the nick except with multi-character usernames
|
||||||
|
containing a zero-width space between the first and second character letter.
|
||||||
|
|
||||||
|
Single character usernames will be left untouched.
|
||||||
|
|
||||||
|
This is useful to prevent remote users from nickalerting
|
||||||
|
IRC users of the same name when the remote user speaks.
|
||||||
|
|
||||||
|
This result can be used in {TENGO} in RemoteNickFormat.
|
||||||
|
*/
|
||||||
|
|
||||||
|
result = nick
|
||||||
|
if len(nick) > 1 {
|
||||||
|
result = string(nick[0]) + "" + nick[1:]
|
||||||
|
}
|
||||||
9
contrib/remotenickformat.tengo
Normal file
9
contrib/remotenickformat.tengo
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
This script will return the current time in kitchen format if the protocol (of the remote bridge) isn't irc
|
||||||
|
See https://github.com/d5/tengo/blob/master/docs/stdlib-times.md
|
||||||
|
This result can be used in {TENGO} in RemoteNickFormat
|
||||||
|
*/
|
||||||
|
times := import("times")
|
||||||
|
if protocol != "irc" {
|
||||||
|
result=times.time_format(times.now(),times.format_kitchen)
|
||||||
|
}
|
||||||
@@ -3,35 +3,41 @@ package bridgemap
|
|||||||
import (
|
import (
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/api"
|
"github.com/42wim/matterbridge/bridge/api"
|
||||||
"github.com/42wim/matterbridge/bridge/discord"
|
bdiscord "github.com/42wim/matterbridge/bridge/discord"
|
||||||
"github.com/42wim/matterbridge/bridge/gitter"
|
bgitter "github.com/42wim/matterbridge/bridge/gitter"
|
||||||
"github.com/42wim/matterbridge/bridge/irc"
|
birc "github.com/42wim/matterbridge/bridge/irc"
|
||||||
"github.com/42wim/matterbridge/bridge/matrix"
|
bmatrix "github.com/42wim/matterbridge/bridge/matrix"
|
||||||
"github.com/42wim/matterbridge/bridge/mattermost"
|
bmattermost "github.com/42wim/matterbridge/bridge/mattermost"
|
||||||
"github.com/42wim/matterbridge/bridge/rocketchat"
|
brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
|
||||||
"github.com/42wim/matterbridge/bridge/slack"
|
bslack "github.com/42wim/matterbridge/bridge/slack"
|
||||||
"github.com/42wim/matterbridge/bridge/sshchat"
|
bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
|
||||||
"github.com/42wim/matterbridge/bridge/steam"
|
bsteam "github.com/42wim/matterbridge/bridge/steam"
|
||||||
"github.com/42wim/matterbridge/bridge/telegram"
|
btelegram "github.com/42wim/matterbridge/bridge/telegram"
|
||||||
"github.com/42wim/matterbridge/bridge/whatsapp"
|
bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
|
||||||
"github.com/42wim/matterbridge/bridge/xmpp"
|
bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
|
||||||
"github.com/42wim/matterbridge/bridge/zulip"
|
bzulip "github.com/42wim/matterbridge/bridge/zulip"
|
||||||
)
|
)
|
||||||
|
|
||||||
var FullMap = map[string]bridge.Factory{
|
var (
|
||||||
"api": api.New,
|
FullMap = map[string]bridge.Factory{
|
||||||
"discord": bdiscord.New,
|
"api": api.New,
|
||||||
"gitter": bgitter.New,
|
"discord": bdiscord.New,
|
||||||
"irc": birc.New,
|
"gitter": bgitter.New,
|
||||||
"mattermost": bmattermost.New,
|
"irc": birc.New,
|
||||||
"matrix": bmatrix.New,
|
"mattermost": bmattermost.New,
|
||||||
"rocketchat": brocketchat.New,
|
"matrix": bmatrix.New,
|
||||||
"slack-legacy": bslack.NewLegacy,
|
"rocketchat": brocketchat.New,
|
||||||
"slack": bslack.New,
|
"slack-legacy": bslack.NewLegacy,
|
||||||
"sshchat": bsshchat.New,
|
"slack": bslack.New,
|
||||||
"steam": bsteam.New,
|
"sshchat": bsshchat.New,
|
||||||
"telegram": btelegram.New,
|
"steam": bsteam.New,
|
||||||
"whatsapp": bwhatsapp.New,
|
"telegram": btelegram.New,
|
||||||
"xmpp": bxmpp.New,
|
"whatsapp": bwhatsapp.New,
|
||||||
"zulip": bzulip.New,
|
"xmpp": bxmpp.New,
|
||||||
}
|
"zulip": bzulip.New,
|
||||||
|
}
|
||||||
|
|
||||||
|
UserTypingSupport = map[string]struct{}{
|
||||||
|
"slack": {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import (
|
|||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/42wim/matterbridge/internal"
|
||||||
"github.com/d5/tengo/script"
|
"github.com/d5/tengo/script"
|
||||||
|
"github.com/d5/tengo/stdlib"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
"github.com/peterhellberg/emojilib"
|
"github.com/peterhellberg/emojilib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@@ -211,23 +213,6 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
|
|||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
|
|
||||||
// irc quit is for the whole bridge, isn't a per channel quit.
|
|
||||||
// channel is empty when we quit
|
|
||||||
if msg.Event == config.EventJoinLeave && getProtocol(msg) == "irc" && msg.Channel == "" {
|
|
||||||
// if we only have one channel on this irc bridge it's got to be the sending one.
|
|
||||||
// don't send it back
|
|
||||||
if dest.Account == msg.Account && len(dest.Channels) == 1 && dest.Protocol == "irc" {
|
|
||||||
return channels
|
|
||||||
}
|
|
||||||
for _, channel := range gw.Channels {
|
|
||||||
if channel.Account == dest.Account && strings.Contains(channel.Direction, "out") &&
|
|
||||||
gw.validGatewayDest(msg) {
|
|
||||||
channels = append(channels, *channel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return channels
|
|
||||||
}
|
|
||||||
|
|
||||||
// if source channel is in only, do nothing
|
// if source channel is in only, do nothing
|
||||||
for _, channel := range gw.Channels {
|
for _, channel := range gw.Channels {
|
||||||
// lookup the channel from the message
|
// lookup the channel from the message
|
||||||
@@ -347,6 +332,11 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri
|
|||||||
nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1)
|
nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1)
|
||||||
nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
|
nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
|
||||||
nick = strings.Replace(nick, "{CHANNEL}", msg.Channel, -1)
|
nick = strings.Replace(nick, "{CHANNEL}", msg.Channel, -1)
|
||||||
|
tengoNick, err := gw.modifyUsernameTengo(msg, br)
|
||||||
|
if err != nil {
|
||||||
|
gw.logger.Errorf("modifyUsernameTengo error: %s", err)
|
||||||
|
}
|
||||||
|
nick = strings.Replace(nick, "{TENGO}", tengoNick, -1) //nolint:gocritic
|
||||||
return nick
|
return nick
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +353,9 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
|
|||||||
if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil {
|
if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil {
|
||||||
gw.logger.Errorf("TengoModifyMessage failed: %s", err)
|
gw.logger.Errorf("TengoModifyMessage failed: %s", err)
|
||||||
}
|
}
|
||||||
|
if err := modifyMessageTengo(gw.BridgeValues().Tengo.Message, msg); err != nil {
|
||||||
|
gw.logger.Errorf("Tengo.Message failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// replace :emoji: to unicode
|
// replace :emoji: to unicode
|
||||||
msg.Text = emojilib.Replace(msg.Text)
|
msg.Text = emojilib.Replace(msg.Text)
|
||||||
@@ -437,6 +430,11 @@ func (gw *Gateway) SendMessage(
|
|||||||
msg.ParentID = "msg-parent-not-found"
|
msg.ParentID = "msg-parent-not-found"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := gw.modifySendMessageTengo(rmsg, &msg, dest)
|
||||||
|
if err != nil {
|
||||||
|
gw.logger.Errorf("modifySendMessageTengo: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// if we are using mattermost plugin account, send messages to MattermostPlugin channel
|
// if we are using mattermost plugin account, send messages to MattermostPlugin channel
|
||||||
// that can be picked up by the mattermost matterbridge plugin
|
// that can be picked up by the mattermost matterbridge plugin
|
||||||
if dest.Account == "mattermost.plugin" {
|
if dest.Account == "mattermost.plugin" {
|
||||||
@@ -503,6 +501,7 @@ func modifyMessageTengo(filename string, msg *config.Message) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s := script.New(res)
|
s := script.New(res)
|
||||||
|
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
||||||
_ = s.Add("msgText", msg.Text)
|
_ = s.Add("msgText", msg.Text)
|
||||||
_ = s.Add("msgUsername", msg.Username)
|
_ = s.Add("msgUsername", msg.Username)
|
||||||
_ = s.Add("msgAccount", msg.Account)
|
_ = s.Add("msgAccount", msg.Account)
|
||||||
@@ -518,3 +517,77 @@ func modifyMessageTengo(filename string, msg *config.Message) error {
|
|||||||
msg.Username = c.Get("msgUsername").String()
|
msg.Username = c.Get("msgUsername").String()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) (string, error) {
|
||||||
|
filename := gw.BridgeValues().Tengo.RemoteNickFormat
|
||||||
|
if filename == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
res, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s := script.New(res)
|
||||||
|
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
||||||
|
_ = s.Add("result", "")
|
||||||
|
_ = s.Add("msgText", msg.Text)
|
||||||
|
_ = s.Add("msgUsername", msg.Username)
|
||||||
|
_ = s.Add("nick", msg.Username)
|
||||||
|
_ = s.Add("msgAccount", msg.Account)
|
||||||
|
_ = s.Add("msgChannel", msg.Channel)
|
||||||
|
_ = s.Add("channel", msg.Channel)
|
||||||
|
_ = s.Add("msgProtocol", msg.Protocol)
|
||||||
|
_ = s.Add("remoteAccount", br.Account)
|
||||||
|
_ = s.Add("protocol", br.Protocol)
|
||||||
|
_ = s.Add("bridge", br.Name)
|
||||||
|
_ = s.Add("gateway", gw.Name)
|
||||||
|
c, err := s.Compile()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err := c.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return c.Get("result").String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.Message, br *bridge.Bridge) error {
|
||||||
|
filename := gw.BridgeValues().Tengo.OutMessage
|
||||||
|
var res []byte
|
||||||
|
var err error
|
||||||
|
if filename == "" {
|
||||||
|
res, err = internal.Asset("tengo/outmessage.tengo")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res, err = ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s := script.New(res)
|
||||||
|
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
||||||
|
_ = s.Add("inAccount", origmsg.Account)
|
||||||
|
_ = s.Add("inProtocol", origmsg.Protocol)
|
||||||
|
_ = s.Add("inChannel", origmsg.Channel)
|
||||||
|
_ = s.Add("inGateway", origmsg.Gateway)
|
||||||
|
_ = s.Add("inEvent", origmsg.Event)
|
||||||
|
_ = s.Add("outAccount", br.Account)
|
||||||
|
_ = s.Add("outProtocol", br.Protocol)
|
||||||
|
_ = s.Add("outChannel", msg.Channel)
|
||||||
|
_ = s.Add("outGateway", gw.Name)
|
||||||
|
_ = s.Add("outEvent", msg.Event)
|
||||||
|
_ = s.Add("msgText", msg.Text)
|
||||||
|
_ = s.Add("msgUsername", msg.Username)
|
||||||
|
c, err := s.Compile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Text = c.Get("msgText").String()
|
||||||
|
msg.Username = c.Get("msgUsername").String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/42wim/matterbridge/gateway/bridgemap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleEventFailure handles failures and reconnects bridges.
|
// handleEventFailure handles failures and reconnects bridges.
|
||||||
@@ -190,6 +191,14 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
|
|||||||
func (gw *Gateway) handleMessage(rmsg *config.Message, dest *bridge.Bridge) []*BrMsgID {
|
func (gw *Gateway) handleMessage(rmsg *config.Message, dest *bridge.Bridge) []*BrMsgID {
|
||||||
var brMsgIDs []*BrMsgID
|
var brMsgIDs []*BrMsgID
|
||||||
|
|
||||||
|
// Not all bridges support "user is typing" indications so skip the message
|
||||||
|
// if the targeted bridge does not support it.
|
||||||
|
if rmsg.Event == config.EventUserTyping {
|
||||||
|
if _, ok := bridgemap.UserTypingSupport[dest.Protocol]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if we have an attached file, or other info
|
// if we have an attached file, or other info
|
||||||
if rmsg.Extra != nil && len(rmsg.Extra[config.EventFileFailureSize]) != 0 && rmsg.Text == "" {
|
if rmsg.Extra != nil && len(rmsg.Extra[config.EventFileFailureSize]) != 0 && rmsg.Text == "" {
|
||||||
return brMsgIDs
|
return brMsgIDs
|
||||||
|
|||||||
10
go.mod
10
go.mod
@@ -6,8 +6,9 @@ require (
|
|||||||
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
|
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
|
||||||
github.com/Jeffail/gabs v1.1.1 // indirect
|
github.com/Jeffail/gabs v1.1.1 // indirect
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.2
|
||||||
github.com/bwmarrin/discordgo v0.19.0
|
github.com/bwmarrin/discordgo v0.19.0
|
||||||
github.com/d5/tengo v1.12.1
|
github.com/d5/tengo v1.20.0
|
||||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
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/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
||||||
@@ -27,12 +28,10 @@ require (
|
|||||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
|
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
|
||||||
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect
|
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect
|
||||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
|
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
|
||||||
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b
|
|
||||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
|
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
|
||||||
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea
|
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea
|
||||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
|
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
|
||||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
|
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
|
||||||
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e
|
|
||||||
github.com/mattermost/mattermost-server v5.5.0+incompatible
|
github.com/mattermost/mattermost-server v5.5.0+incompatible
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
|
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
|
||||||
@@ -45,7 +44,6 @@ require (
|
|||||||
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
|
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
|
||||||
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
|
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
|
||||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320
|
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320
|
||||||
github.com/pkg/errors v0.8.0 // indirect
|
|
||||||
github.com/rs/xid v1.2.1
|
github.com/rs/xid v1.2.1
|
||||||
github.com/russross/blackfriday v1.5.2
|
github.com/russross/blackfriday v1.5.2
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
||||||
@@ -68,6 +66,10 @@ require (
|
|||||||
go.uber.org/multierr v1.1.0 // indirect
|
go.uber.org/multierr v1.1.0 // indirect
|
||||||
go.uber.org/zap v1.9.1 // indirect
|
go.uber.org/zap v1.9.1 // indirect
|
||||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9
|
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9
|
||||||
|
golang.org/x/net v0.0.0-20190110200230-915654e7eabc // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
|||||||
40
go.sum
40
go.sum
@@ -8,6 +8,13 @@ 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/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
|
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/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.0.2 h1:MelwdquHuuNObBGV7CpXbky2aVdilx/CwiXMwZvS74U=
|
||||||
|
github.com/Rhymen/go-whatsapp v0.0.2/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
|
||||||
|
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/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
|
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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
|
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
|
||||||
@@ -15,8 +22,8 @@ github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVO
|
|||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/d5/tengo v1.12.1 h1:libKkDM95CsZgYs6E5eiEaM9sbcw2EzJRSkr9o5NO4s=
|
github.com/d5/tengo v1.20.0 h1:lFmktzEGR6khlZu2MHUWJ5oDWS4l3jNRV/OhclZgcYc=
|
||||||
github.com/d5/tengo v1.12.1/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY=
|
github.com/d5/tengo v1.20.0/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -24,21 +31,19 @@ github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX
|
|||||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
|
github.com/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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
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 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 v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
|
github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
|
||||||
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
|
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
|
||||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
|
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/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
|
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
|
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
|
||||||
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
@@ -78,8 +83,6 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe
|
|||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/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 h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs=
|
||||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
|
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
|
||||||
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b h1:cO6Z+yj4Ivq/ay/IxSrV90oSIW/SSXWLa+XHsiLKMrw=
|
|
||||||
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b/go.mod h1:dW19fYkkdUZsBAx7zv9fDh0n6NRqYIaKwB2JEBw8d0U=
|
|
||||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k=
|
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-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
|
||||||
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc=
|
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc=
|
||||||
@@ -88,8 +91,6 @@ github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtW
|
|||||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
|
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 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
|
||||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
|
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
|
||||||
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e h1:1NqciL8sz+0UYeFrd/UQlL8tJPhFxOBmg+a94DN2sJU=
|
|
||||||
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e/go.mod h1:DrIFGcFumRlEW5k3PJjWGKPd4+w37d3SwOxlh1ZAL+4=
|
|
||||||
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
|
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/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.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
@@ -98,7 +99,6 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
|
|||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
@@ -125,22 +125,20 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
|
|||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0=
|
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0=
|
||||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA=
|
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA=
|
||||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
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/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 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
|
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
|
||||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
|
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
|
||||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio=
|
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio=
|
||||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
|
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
||||||
@@ -199,22 +197,26 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 h1:+vH8qNweCrORN49012OX3h0oWEXO3p+rRnpAGQinddk=
|
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 h1:+vH8qNweCrORN49012OX3h0oWEXO3p+rRnpAGQinddk=
|
||||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
|
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
|
||||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o=
|
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o=
|
||||||
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -226,7 +228,3 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
|||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
|
||||||
maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI=
|
|
||||||
maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI=
|
|
||||||
maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM=
|
|
||||||
|
|||||||
288
internal/bindata.go
Normal file
288
internal/bindata.go
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
// Code generated by go-bindata. DO NOT EDIT.
|
||||||
|
// sources:
|
||||||
|
// tengo/outmessage.tengo
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, gz)
|
||||||
|
clErr := gz.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if clErr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
bytes []byte
|
||||||
|
info fileInfoEx
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileInfoEx interface {
|
||||||
|
os.FileInfo
|
||||||
|
MD5Checksum() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type bindataFileInfo struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
mode os.FileMode
|
||||||
|
modTime time.Time
|
||||||
|
md5checksum string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi bindataFileInfo) Name() string {
|
||||||
|
return fi.name
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
|
return fi.size
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
|
return fi.mode
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
|
return fi.modTime
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) MD5Checksum() string {
|
||||||
|
return fi.md5checksum
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _bindataTengoOutmessagetengo = []byte(
|
||||||
|
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x91\x3d\x8f\xda\x40\x10\x86\xfb\xfd\x15\x13\x37\xb1\x2d\x07\xe7\xa3" +
|
||||||
|
"\xb3\x64\x59\x11\x45\x94\x2e\x8a\x92\x0a\xd0\xb1\xac\x07\x33\xd2\x7a\xc7\x1a\x8f\x31\x88\xe3\xbf\x9f\xcc\x01\x47" +
|
||||||
|
"\x7f\xc5\x75\xef\xae\x9e\x9d\x77\x1f\x4d\x9e\x9a\xbd\x15\xb2\x1b\x8f\x3d\xd8\xbd\x25\x3f\x45\x30\x82\xb6\xfe\xc2" +
|
||||||
|
"\xc1\x1f\x0b\x43\xe1\xa7\x73\x3c\x04\xcd\x80\xc2\x1f\x61\x65\xc7\x7e\xca\xf3\x9d\x0d\x01\x2f\xf1\x97\x55\x1c\xed" +
|
||||||
|
"\xd1\xf0\xa0\x77\x98\x07\x7d\xa3\x79\xd0\x3b\xce\x83\xde\xf8\xd7\x9e\x51\x48\xb1\x30\x6d\xdf\xfc\xc3\x83\x66\xd0" +
|
||||||
|
"\xf6\xcd\xff\x1e\x25\xd8\x16\x4d\x9a\x1b\xa3\x78\x50\x28\x4a\xa0\xb6\x63\xd1\x38\x9a\xce\x51\x62\x4c\x9e\x43\xaf" +
|
||||||
|
"\x42\x1d\x90\x38\x70\xec\x59\xfa\xe9\x8e\xb6\x30\xe2\x67\x41\x08\xac\xd0\x63\xa8\x29\x34\xa0\x0c\x36\x5c\xc0\x8d" +
|
||||||
|
"\x50\xdd\x20\x8c\x78\x7d\xac\x3b\x84\xdf\x7f\xe7\xb7\x01\xb4\x7d\xd0\x84\xb2\x84\x88\xc4\x45\x70\x32\x00\x00\x82" +
|
||||||
|
"\xd3\x3f\xa6\xfe\x99\xe0\x93\xe3\xb6\x23\x8f\xf1\x7a\x79\xf8\xfa\x23\xae\x8a\x65\x7d\xfa\x96\x7d\x3f\xc7\x55\x91" +
|
||||||
|
"\x5d\x63\x52\x25\xd5\xf3\x62\x51\xb8\xa0\xe2\x8b\xd5\x6a\x9d\x5c\xc6\x5c\x4d\x4b\xc1\x99\x60\xe7\xad\xc3\xf8\x26" +
|
||||||
|
"\x1f\x45\x89\x39\x9b\xf7\x6b\xe4\x29\x6d\x1f\x57\x00\x9f\x3e\xc6\x24\xcd\xcd\x4b\x00\x00\x00\xff\xff\x40\xb8\x54" +
|
||||||
|
"\xb8\x64\x02\x00\x00")
|
||||||
|
|
||||||
|
func bindataTengoOutmessagetengoBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
_bindataTengoOutmessagetengo,
|
||||||
|
"tengo/outmessage.tengo",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func bindataTengoOutmessagetengo() (*asset, error) {
|
||||||
|
bytes, err := bindataTengoOutmessagetengoBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{
|
||||||
|
name: "tengo/outmessage.tengo",
|
||||||
|
size: 612,
|
||||||
|
md5checksum: "",
|
||||||
|
mode: os.FileMode(420),
|
||||||
|
modTime: time.Unix(1555622139, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
//
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.bytes, nil
|
||||||
|
}
|
||||||
|
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MustAsset is like Asset but panics when Asset would return an error.
|
||||||
|
// It simplifies safe initialization of global variables.
|
||||||
|
// nolint: deadcode
|
||||||
|
//
|
||||||
|
func MustAsset(name string) []byte {
|
||||||
|
a, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
panic("asset: Asset(" + name + "): " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AssetInfo loads and returns the asset info for the given name.
|
||||||
|
// It returns an error if the asset could not be found or could not be loaded.
|
||||||
|
//
|
||||||
|
func AssetInfo(name string) (os.FileInfo, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.info, nil
|
||||||
|
}
|
||||||
|
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
// nolint: deadcode
|
||||||
|
//
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
//
|
||||||
|
var _bindata = map[string]func() (*asset, error){
|
||||||
|
"tengo/outmessage.tengo": bindataTengoOutmessagetengo,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
//
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(cannonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, &os.PathError{
|
||||||
|
Op: "open",
|
||||||
|
Path: name,
|
||||||
|
Err: os.ErrNotExist,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, &os.PathError{
|
||||||
|
Op: "open",
|
||||||
|
Path: name,
|
||||||
|
Err: os.ErrNotExist,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for childName := range node.Children {
|
||||||
|
rv = append(rv, childName)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type bintree struct {
|
||||||
|
Func func() (*asset, error)
|
||||||
|
Children map[string]*bintree
|
||||||
|
}
|
||||||
|
|
||||||
|
var _bintree = &bintree{Func: nil, Children: map[string]*bintree{
|
||||||
|
"tengo": {Func: nil, Children: map[string]*bintree{
|
||||||
|
"outmessage.tengo": {Func: bindataTengoOutmessagetengo, Children: map[string]*bintree{}},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
// RestoreAsset restores an asset under the given directory
|
||||||
|
func RestoreAsset(dir, name string) error {
|
||||||
|
data, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := AssetInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreAssets restores an asset under the given directory recursively
|
||||||
|
func RestoreAssets(dir, name string) error {
|
||||||
|
children, err := AssetDir(name)
|
||||||
|
// File
|
||||||
|
if err != nil {
|
||||||
|
return RestoreAsset(dir, name)
|
||||||
|
}
|
||||||
|
// Dir
|
||||||
|
for _, child := range children {
|
||||||
|
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _filePath(dir, name string) string {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||||
|
}
|
||||||
19
internal/tengo/outmessage.tengo
Normal file
19
internal/tengo/outmessage.tengo
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
variables available
|
||||||
|
read-only:
|
||||||
|
inAccount, inProtocol, inChannel, inGateway
|
||||||
|
outAccount, outProtocol, outChannel, outGateway
|
||||||
|
|
||||||
|
read-write:
|
||||||
|
msgText, msgUsername
|
||||||
|
*/
|
||||||
|
|
||||||
|
text := import("text")
|
||||||
|
|
||||||
|
// start - strip irc colors
|
||||||
|
// if we're not sending to an irc bridge we strip the IRC colors
|
||||||
|
if inProtocol == "irc" {
|
||||||
|
re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
|
||||||
|
msgText=re.replace(msgText,"")
|
||||||
|
}
|
||||||
|
// end - strip irc colors
|
||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "1.14.1"
|
version = "1.15.0"
|
||||||
githash string
|
githash string
|
||||||
|
|
||||||
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
|
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#This is configuration for matterbridge.
|
#This is configuration for matterbridge.
|
||||||
#WARNING: as this file contains credentials, be sure to set correct file permissions
|
#WARNING: as this file contains credentials, be sure to set correct file permissions
|
||||||
|
#See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for how to create your config
|
||||||
|
#See https://github.com/42wim/matterbridge/wiki/Settings for all settings
|
||||||
###################################################################
|
###################################################################
|
||||||
#IRC section
|
#IRC section
|
||||||
###################################################################
|
###################################################################
|
||||||
@@ -27,7 +29,7 @@ UseTLS=false
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
UseSASL=false
|
UseSASL=false
|
||||||
|
|
||||||
#Enable to not verify the certificate on your irc server. i
|
#Enable to not verify the certificate on your irc server.
|
||||||
#e.g. when using selfsigned certificates
|
#e.g. when using selfsigned certificates
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
SkipTLSVerify=true
|
SkipTLSVerify=true
|
||||||
@@ -155,6 +157,11 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowJoinPart=false
|
ShowJoinPart=false
|
||||||
|
|
||||||
|
#Enable to show verbose users joins/parts (ident@host) from other bridges
|
||||||
|
#Currently works for messages from the following bridges: irc
|
||||||
|
#OPTIONAL (default false)
|
||||||
|
VerboseJoinPart=false
|
||||||
|
|
||||||
#Do not send joins/parts to other bridges
|
#Do not send joins/parts to other bridges
|
||||||
#Currently works for messages from the following bridges: irc, mattermost, slack
|
#Currently works for messages from the following bridges: irc, mattermost, slack
|
||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
@@ -270,98 +277,6 @@ StripNick=false
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowTopicChange=false
|
ShowTopicChange=false
|
||||||
|
|
||||||
###################################################################
|
|
||||||
#hipchat section
|
|
||||||
###################################################################
|
|
||||||
#Go to https://www.hipchat.com/account/xmpp this will show you the necessary data
|
|
||||||
#to fill in the section below
|
|
||||||
[xmpp.hipchat]
|
|
||||||
#xmpp server to connect to.
|
|
||||||
#REQUIRED
|
|
||||||
Server="chat.hipchat.com:5222"
|
|
||||||
|
|
||||||
#Jabber ID
|
|
||||||
#REQUIRED
|
|
||||||
Jid="12345_12345@chat.hipchat.com"
|
|
||||||
|
|
||||||
#Password (your hipchat password)
|
|
||||||
#REQUIRED
|
|
||||||
Password="yourpass"
|
|
||||||
|
|
||||||
#Conference (MUC) domain
|
|
||||||
#REQUIRED
|
|
||||||
Muc="conf.hipchat.com"
|
|
||||||
|
|
||||||
#Room nickname
|
|
||||||
#REQUIRED
|
|
||||||
Nick="yourlogin"
|
|
||||||
|
|
||||||
## 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="spammer1 spammer2"
|
|
||||||
|
|
||||||
#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}/{BRIDGE}] <{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
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#mattermost section
|
#mattermost section
|
||||||
###################################################################
|
###################################################################
|
||||||
@@ -1480,6 +1395,7 @@ RemoteNickFormat="{NICK}"
|
|||||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||||
#The string "{GATEWAY}" (case sensitive) will be replaced by the origin gateway name that is replicating the message.
|
#The string "{GATEWAY}" (case sensitive) will be replaced by the origin gateway name that is replicating the message.
|
||||||
#The string "{CHANNEL}" (case sensitive) will be replaced by the origin channel name used by the bridge
|
#The string "{CHANNEL}" (case sensitive) will be replaced by the origin channel name used by the bridge
|
||||||
|
#The string "{TENGO}" (case sensitive) will be replaced by the output of the RemoteNickFormat script under [tengo]
|
||||||
#OPTIONAL (default empty)
|
#OPTIONAL (default empty)
|
||||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||||
|
|
||||||
@@ -1529,8 +1445,14 @@ MediaDownloadBlacklist=[".html$",".htm$"]
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
IgnoreFailureOnStart=false
|
IgnoreFailureOnStart=false
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
#Tengo configuration
|
||||||
|
###################################################################
|
||||||
|
#More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and
|
||||||
|
#https://github.com/d5/tengo/blob/master/docs/stdlib.md
|
||||||
|
|
||||||
#TengoModifyMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
[tengo]
|
||||||
|
#InMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
||||||
#This script will receive every incoming message and can be used to modify the Username and the Text of that message.
|
#This script will receive every incoming message and can be used to modify the Username and the Text of that message.
|
||||||
#The script will have the following global variables:
|
#The script will have the following global variables:
|
||||||
#to modify: msgUsername and msgText
|
#to modify: msgUsername and msgText
|
||||||
@@ -1547,10 +1469,42 @@ IgnoreFailureOnStart=false
|
|||||||
# msgText="replaced by this"
|
# msgText="replaced by this"
|
||||||
# msgUsername="fakeuser"
|
# msgUsername="fakeuser"
|
||||||
#}
|
#}
|
||||||
#More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and
|
|
||||||
#https://github.com/d5/tengo/blob/master/docs/stdlib.md
|
|
||||||
#OPTIONAL (default empty)
|
#OPTIONAL (default empty)
|
||||||
TengoModifyMessage="example.tengo"
|
InMessage="example.tengo"
|
||||||
|
|
||||||
|
#OutMessage allows you to specify the location of the script that
|
||||||
|
#will be invoked on each message being sent to a bridge and can be used to modify the Username
|
||||||
|
#and the Text of that message.
|
||||||
|
#
|
||||||
|
#The script will have the following global variables:
|
||||||
|
#read-only:
|
||||||
|
#inAccount, inProtocol, inChannel, inGateway, inEvent
|
||||||
|
#outAccount, outProtocol, outChannel, outGateway, outEvent
|
||||||
|
#
|
||||||
|
#read-write:
|
||||||
|
#msgText, msgUsername
|
||||||
|
#
|
||||||
|
#The script is reloaded on every message, so you can modify the script on the fly.
|
||||||
|
#
|
||||||
|
#The default script in https://github.com/42wim/matterbridge/tree/master/internal/tengo/outmessage.tengo
|
||||||
|
#is compiled in and will be executed if no script is specified.
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
OutMessage="example.tengo"
|
||||||
|
|
||||||
|
|
||||||
|
#RemoteNickFormat allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
||||||
|
#The script will have the following global variables:
|
||||||
|
#to modify: result
|
||||||
|
#to read: channel, bridge, gateway, protocol, nick
|
||||||
|
#
|
||||||
|
#The result will be set in {TENGO} in the RemoteNickFormat key of every bridge where {TENGO} is specified
|
||||||
|
#
|
||||||
|
#The script is reloaded on every message, so you can modify the script on the fly.
|
||||||
|
#
|
||||||
|
#Example script can be found in https://github.com/42wim/matterbridge/tree/master/contrib/remotenickformat.tengo
|
||||||
|
#
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
RemoteNickFormat="remotenickformat.tengo"
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#Gateway configuration
|
#Gateway configuration
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ func (m *MMClient) GetChannelId(name string, teamId string) string { //nolint:go
|
|||||||
if res == name {
|
if res == name {
|
||||||
return channel.Id
|
return channel.Id
|
||||||
}
|
}
|
||||||
|
} else if channel.Name == name {
|
||||||
|
return channel.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
@@ -166,23 +167,42 @@ func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MMClient) UpdateChannelsTeam(teamID string) error {
|
||||||
|
mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "")
|
||||||
|
if resp.Error != nil {
|
||||||
|
return errors.New(resp.Error.DetailedError)
|
||||||
|
}
|
||||||
|
for idx, t := range m.OtherTeams {
|
||||||
|
if t.Id == teamID {
|
||||||
|
m.Lock()
|
||||||
|
m.OtherTeams[idx].Channels = mmchannels
|
||||||
|
m.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mmchannels, resp = m.Client.GetPublicChannelsForTeam(teamID, 0, 5000, "")
|
||||||
|
if resp.Error != nil {
|
||||||
|
return errors.New(resp.Error.DetailedError)
|
||||||
|
}
|
||||||
|
for idx, t := range m.OtherTeams {
|
||||||
|
if t.Id == teamID {
|
||||||
|
m.Lock()
|
||||||
|
m.OtherTeams[idx].MoreChannels = mmchannels
|
||||||
|
m.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MMClient) UpdateChannels() error {
|
func (m *MMClient) UpdateChannels() error {
|
||||||
mmchannels, resp := m.Client.GetChannelsForTeamForUser(m.Team.Id, m.User.Id, "")
|
if err := m.UpdateChannelsTeam(m.Team.Id); err != nil {
|
||||||
if resp.Error != nil {
|
return err
|
||||||
return errors.New(resp.Error.DetailedError)
|
|
||||||
}
|
}
|
||||||
m.Lock()
|
for _, t := range m.OtherTeams {
|
||||||
m.Team.Channels = mmchannels
|
if err := m.UpdateChannelsTeam(t.Id); err != nil {
|
||||||
m.Unlock()
|
return err
|
||||||
|
}
|
||||||
mmchannels, resp = m.Client.GetPublicChannelsForTeam(m.Team.Id, 0, 5000, "")
|
|
||||||
if resp.Error != nil {
|
|
||||||
return errors.New(resp.Error.DetailedError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Lock()
|
|
||||||
m.Team.MoreChannels = mmchannels
|
|
||||||
m.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,14 +132,25 @@ func (m *MMClient) initUser() error {
|
|||||||
return resp.Error
|
return resp.Error
|
||||||
}
|
}
|
||||||
for _, team := range teams {
|
for _, team := range teams {
|
||||||
mmusers, resp := m.Client.GetUsersInTeam(team.Id, 0, 50000, "")
|
idx := 0
|
||||||
|
max := 200
|
||||||
|
usermap := make(map[string]*model.User)
|
||||||
|
mmusers, resp := m.Client.GetUsersInTeam(team.Id, idx, max, "")
|
||||||
if resp.Error != nil {
|
if resp.Error != nil {
|
||||||
return errors.New(resp.Error.DetailedError)
|
return errors.New(resp.Error.DetailedError)
|
||||||
}
|
}
|
||||||
usermap := make(map[string]*model.User)
|
for len(mmusers) > 0 {
|
||||||
for _, user := range mmusers {
|
for _, user := range mmusers {
|
||||||
usermap[user.Id] = user
|
usermap[user.Id] = user
|
||||||
|
}
|
||||||
|
mmusers, resp = m.Client.GetUsersInTeam(team.Id, idx, max, "")
|
||||||
|
if resp.Error != nil {
|
||||||
|
return errors.New(resp.Error.DetailedError)
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
}
|
}
|
||||||
|
m.logger.Infof("found %d users in team %s", len(usermap), team.Name)
|
||||||
|
|
||||||
t := &Team{Team: team, Users: usermap, Id: team.Id}
|
t := &Team{Team: team, Users: usermap, Id: team.Id}
|
||||||
|
|
||||||
|
|||||||
@@ -216,9 +216,17 @@ func (m *MMClient) WsReceiver() {
|
|||||||
if msg.Post != nil {
|
if msg.Post != nil {
|
||||||
if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" {
|
if msg.Text != "" || len(msg.Post.FileIds) > 0 || msg.Post.Type == "slack_attachment" {
|
||||||
m.MessageChan <- msg
|
m.MessageChan <- msg
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
switch msg.Raw.Event {
|
||||||
|
case model.WEBSOCKET_EVENT_USER_ADDED,
|
||||||
|
model.WEBSOCKET_EVENT_USER_REMOVED,
|
||||||
|
model.WEBSOCKET_EVENT_CHANNEL_CREATED,
|
||||||
|
model.WEBSOCKET_EVENT_CHANNEL_DELETED:
|
||||||
|
m.MessageChan <- msg
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var response model.WebSocketResponse
|
var response model.WebSocketResponse
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func (m *MMClient) DeleteMessage(postId string) error { //nolint:golint
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint
|
func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint
|
||||||
post := &model.Post{Message: text}
|
post := &model.Post{Message: text, Id: postId}
|
||||||
res, resp := m.Client.UpdatePost(postId, post)
|
res, resp := m.Client.UpdatePost(postId, post)
|
||||||
if resp.Error != nil {
|
if resp.Error != nil {
|
||||||
return "", resp.Error
|
return "", resp.Error
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package matterclient
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
)
|
)
|
||||||
@@ -99,15 +100,25 @@ func (m *MMClient) GetUsers() map[string]*model.User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MMClient) UpdateUsers() error {
|
func (m *MMClient) UpdateUsers() error {
|
||||||
mmusers, resp := m.Client.GetUsers(0, 50000, "")
|
idx := 0
|
||||||
|
max := 200
|
||||||
|
mmusers, resp := m.Client.GetUsers(idx, max, "")
|
||||||
if resp.Error != nil {
|
if resp.Error != nil {
|
||||||
return errors.New(resp.Error.DetailedError)
|
return errors.New(resp.Error.DetailedError)
|
||||||
}
|
}
|
||||||
m.Lock()
|
for len(mmusers) > 0 {
|
||||||
for _, user := range mmusers {
|
m.Lock()
|
||||||
m.Users[user.Id] = user
|
for _, user := range mmusers {
|
||||||
|
m.Users[user.Id] = user
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
mmusers, resp = m.Client.GetUsers(idx, max, "")
|
||||||
|
time.Sleep(time.Millisecond * 300)
|
||||||
|
if resp.Error != nil {
|
||||||
|
return errors.New(resp.Error.DetailedError)
|
||||||
|
}
|
||||||
|
idx++
|
||||||
}
|
}
|
||||||
m.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
.idea/
|
.idea/
|
||||||
docs/
|
docs/
|
||||||
|
build/
|
||||||
@@ -3,7 +3,7 @@ Package rhymen/go-whatsapp implements the WhatsApp Web API to provide a clean in
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
```sh
|
```sh
|
||||||
go get github.com/rhymen/go-whatsapp
|
go get github.com/Rhymen/go-whatsapp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -30,7 +30,7 @@ The authentication process requires you to scan the qr code, that is send throug
|
|||||||
|
|
||||||
### Restore
|
### Restore
|
||||||
```go
|
```go
|
||||||
newSess, err := wac.RestoreSession(sess)
|
newSess, err := wac.RestoreWithSession(sess)
|
||||||
```
|
```
|
||||||
The restore function needs a valid session and returns the new session that was created.
|
The restore function needs a valid session and returns the new session that was created.
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ package binary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matterbridge/go-whatsapp/binary/token"
|
"github.com/Rhymen/go-whatsapp/binary/token"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -2,7 +2,7 @@ package binary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matterbridge/go-whatsapp/binary/token"
|
"github.com/Rhymen/go-whatsapp/binary/token"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -2,7 +2,7 @@ package binary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
pb "github.com/matterbridge/go-whatsapp/binary/proto"
|
pb "github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
210
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
Normal file
210
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
|
||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metric byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
debugLog metric = iota + 1
|
||||||
|
queryResume
|
||||||
|
queryReceipt
|
||||||
|
queryMedia
|
||||||
|
queryChat
|
||||||
|
queryContacts
|
||||||
|
queryMessages
|
||||||
|
presence
|
||||||
|
presenceSubscribe
|
||||||
|
group
|
||||||
|
read
|
||||||
|
chat
|
||||||
|
received
|
||||||
|
pic
|
||||||
|
status
|
||||||
|
message
|
||||||
|
queryActions
|
||||||
|
block
|
||||||
|
queryGroup
|
||||||
|
queryPreview
|
||||||
|
queryEmoji
|
||||||
|
queryMessageInfo
|
||||||
|
spam
|
||||||
|
querySearch
|
||||||
|
queryIdentity
|
||||||
|
queryUrl
|
||||||
|
profile
|
||||||
|
contact
|
||||||
|
queryVcard
|
||||||
|
queryStatus
|
||||||
|
queryStatusUpdate
|
||||||
|
privacyStatus
|
||||||
|
queryLiveLocations
|
||||||
|
liveLocation
|
||||||
|
queryVname
|
||||||
|
queryLabels
|
||||||
|
call
|
||||||
|
queryCall
|
||||||
|
queryQuickReplies
|
||||||
|
)
|
||||||
|
|
||||||
|
type flag byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
ignore flag = 1 << (7 - iota)
|
||||||
|
ackRequest
|
||||||
|
available
|
||||||
|
notAvailable
|
||||||
|
expires
|
||||||
|
skipOffline
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
|
||||||
|
It holds all necessary information to make the package work internally.
|
||||||
|
*/
|
||||||
|
type Conn struct {
|
||||||
|
ws *websocketWrapper
|
||||||
|
listener *listenerWrapper
|
||||||
|
|
||||||
|
connected bool
|
||||||
|
loggedIn bool
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
|
||||||
|
session *Session
|
||||||
|
sessionLock uint32
|
||||||
|
handler []Handler
|
||||||
|
msgCount int
|
||||||
|
msgTimeout time.Duration
|
||||||
|
Info *Info
|
||||||
|
Store *Store
|
||||||
|
ServerLastSeen time.Time
|
||||||
|
|
||||||
|
longClientName string
|
||||||
|
shortClientName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type websocketWrapper struct {
|
||||||
|
sync.Mutex
|
||||||
|
conn *websocket.Conn
|
||||||
|
close chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type listenerWrapper struct {
|
||||||
|
sync.RWMutex
|
||||||
|
m map[string]chan string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
|
||||||
|
The goroutine for handling incoming messages is started
|
||||||
|
*/
|
||||||
|
func NewConn(timeout time.Duration) (*Conn, error) {
|
||||||
|
wac := &Conn{
|
||||||
|
handler: make([]Handler, 0),
|
||||||
|
msgCount: 0,
|
||||||
|
msgTimeout: timeout,
|
||||||
|
Store: newStore(),
|
||||||
|
|
||||||
|
longClientName: "github.com/rhymen/go-whatsapp",
|
||||||
|
shortClientName: "go-whatsapp",
|
||||||
|
}
|
||||||
|
return wac, wac.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect should be guarded with wsWriteMutex
|
||||||
|
func (wac *Conn) connect() (err error) {
|
||||||
|
if wac.connected {
|
||||||
|
return ErrAlreadyConnected
|
||||||
|
}
|
||||||
|
wac.connected = true
|
||||||
|
defer func() { // set connected to false on error
|
||||||
|
if err != nil {
|
||||||
|
wac.connected = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dialer := &websocket.Dialer{
|
||||||
|
ReadBufferSize: 25 * 1024 * 1024,
|
||||||
|
WriteBufferSize: 10 * 1024 * 1024,
|
||||||
|
HandshakeTimeout: wac.msgTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
|
||||||
|
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "couldn't dial whatsapp web websocket")
|
||||||
|
}
|
||||||
|
|
||||||
|
wsConn.SetCloseHandler(func(code int, text string) error {
|
||||||
|
// from default CloseHandler
|
||||||
|
message := websocket.FormatCloseMessage(code, "")
|
||||||
|
err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
||||||
|
|
||||||
|
// our close handling
|
||||||
|
_, _ = wac.Disconnect()
|
||||||
|
wac.handle(&ErrConnectionClosed{Code: code, Text: text})
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
wac.ws = &websocketWrapper{
|
||||||
|
conn: wsConn,
|
||||||
|
close: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.listener = &listenerWrapper{
|
||||||
|
m: make(map[string]chan string),
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.wg = &sync.WaitGroup{}
|
||||||
|
wac.wg.Add(2)
|
||||||
|
go wac.readPump()
|
||||||
|
go wac.keepAlive(20000, 60000)
|
||||||
|
|
||||||
|
wac.loggedIn = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) Disconnect() (Session, error) {
|
||||||
|
if !wac.connected {
|
||||||
|
return Session{}, ErrNotConnected
|
||||||
|
}
|
||||||
|
wac.connected = false
|
||||||
|
wac.loggedIn = false
|
||||||
|
|
||||||
|
close(wac.ws.close) //signal close
|
||||||
|
wac.wg.Wait() //wait for close
|
||||||
|
|
||||||
|
err := wac.ws.conn.Close()
|
||||||
|
wac.ws = nil
|
||||||
|
|
||||||
|
if wac.session == nil {
|
||||||
|
return Session{}, err
|
||||||
|
}
|
||||||
|
return *wac.session, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
|
||||||
|
defer wac.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := wac.sendKeepAlive()
|
||||||
|
if err != nil {
|
||||||
|
wac.handle(errors.Wrap(err, "keepAlive failed"))
|
||||||
|
//TODO: Consequences?
|
||||||
|
}
|
||||||
|
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Duration(interval) * time.Millisecond):
|
||||||
|
case <-wac.ws.close:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ package whatsapp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matterbridge/go-whatsapp/binary"
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -18,21 +18,21 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
//TODO: filename? WhatsApp uses Store.Contacts for these functions
|
//TODO: filename? WhatsApp uses Store.Contacts for these functions
|
||||||
//TODO: functions probably shouldn't return a string, maybe build a struct / return json
|
// functions probably shouldn't return a string, maybe build a struct / return json
|
||||||
//TODO: check for further queries
|
// check for further queries
|
||||||
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
|
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
|
||||||
data := []interface{}{"query", "ProfilePicThumb", jid}
|
data := []interface{}{"query", "ProfilePicThumb", jid}
|
||||||
return wac.write(data)
|
return wac.writeJson(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
|
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
|
||||||
data := []interface{}{"query", "Status", jid}
|
data := []interface{}{"query", "Status", jid}
|
||||||
return wac.write(data)
|
return wac.writeJson(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
|
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
|
||||||
data := []interface{}{"action", "presence", "subscribe", jid}
|
data := []interface{}{"action", "presence", "subscribe", jid}
|
||||||
return wac.write(data)
|
return wac.writeJson(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) {
|
func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) {
|
||||||
@@ -84,7 +84,7 @@ func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error)
|
|||||||
|
|
||||||
func (wac *Conn) Exist(jid string) (<-chan string, error) {
|
func (wac *Conn) Exist(jid string) (<-chan string, error) {
|
||||||
data := []interface{}{"query", "exist", jid}
|
data := []interface{}{"query", "exist", jid}
|
||||||
return wac.write(data)
|
return wac.writeJson(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) Emoji() (*binary.Node, error) {
|
func (wac *Conn) Emoji() (*binary.Node, error) {
|
||||||
@@ -9,7 +9,6 @@ second stage "expands" this key into several additional pseudorandom keys (the o
|
|||||||
package hkdf
|
package hkdf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
@@ -20,33 +19,29 @@ import (
|
|||||||
Expand expands a given key with the HKDF algorithm.
|
Expand expands a given key with the HKDF algorithm.
|
||||||
*/
|
*/
|
||||||
func Expand(key []byte, length int, info string) ([]byte, error) {
|
func Expand(key []byte, length int, info string) ([]byte, error) {
|
||||||
|
var h io.Reader
|
||||||
if info == "" {
|
if info == "" {
|
||||||
keyBlock := hmac.New(sha256.New, key)
|
/*
|
||||||
var out, last []byte
|
Only used during initial login
|
||||||
|
Pseudorandom Key is provided by server and has not to be created
|
||||||
var blockIndex byte = 1
|
*/
|
||||||
for i := 0; len(out) < length; i++ {
|
h = hkdf.Expand(sha256.New, key, []byte(info))
|
||||||
keyBlock.Reset()
|
|
||||||
//keyBlock.Write(append(append(last, []byte(info)...), blockIndex))
|
|
||||||
keyBlock.Write(last)
|
|
||||||
keyBlock.Write([]byte(info))
|
|
||||||
keyBlock.Write([]byte{blockIndex})
|
|
||||||
last = keyBlock.Sum(nil)
|
|
||||||
blockIndex += 1
|
|
||||||
out = append(out, last...)
|
|
||||||
}
|
|
||||||
return out[:length], nil
|
|
||||||
} else {
|
} else {
|
||||||
h := hkdf.New(sha256.New, key, nil, []byte(info))
|
/*
|
||||||
out := make([]byte, length)
|
Used every other time
|
||||||
n, err := io.ReadAtLeast(h, out, length)
|
Pseudorandom Key is created during kdf.New
|
||||||
if err != nil {
|
This is the normal that crypto/hkdf is used
|
||||||
return nil, err
|
*/
|
||||||
}
|
h = hkdf.New(sha256.New, key, nil, []byte(info))
|
||||||
if n != length {
|
|
||||||
return nil, fmt.Errorf("new key to short")
|
|
||||||
}
|
|
||||||
|
|
||||||
return out[:length], nil
|
|
||||||
}
|
}
|
||||||
|
out := make([]byte, length)
|
||||||
|
n, err := io.ReadAtLeast(h, out, length)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n != length {
|
||||||
|
return nil, fmt.Errorf("new key to short")
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
35
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
Normal file
35
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAlreadyConnected = errors.New("already connected")
|
||||||
|
ErrAlreadyLoggedIn = errors.New("already logged in")
|
||||||
|
ErrInvalidSession = errors.New("invalid session")
|
||||||
|
ErrLoginInProgress = errors.New("login or restore already running")
|
||||||
|
ErrNotConnected = errors.New("not connected")
|
||||||
|
ErrInvalidWsData = errors.New("received invalid data")
|
||||||
|
ErrConnectionTimeout = errors.New("connection timed out")
|
||||||
|
ErrMissingMessageTag = errors.New("no messageTag specified or to short")
|
||||||
|
ErrInvalidHmac = errors.New("invalid hmac")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrConnectionFailed struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrConnectionFailed) Error() string {
|
||||||
|
return fmt.Sprintf("connection to WhatsApp servers failed: %v", e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrConnectionClosed struct {
|
||||||
|
Code int
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrConnectionClosed) Error() string {
|
||||||
|
return fmt.Sprintf("server closed connection,code: %d,text: %s", e.Code, e.Text)
|
||||||
|
}
|
||||||
12
vendor/github.com/Rhymen/go-whatsapp/go.mod
generated
vendored
Normal file
12
vendor/github.com/Rhymen/go-whatsapp/go.mod
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module github.com/Rhymen/go-whatsapp
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d // indirect
|
||||||
|
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d // indirect
|
||||||
|
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/pkg/errors v0.8.1
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||||
|
)
|
||||||
35
vendor/github.com/Rhymen/go-whatsapp/go.sum
generated
vendored
Normal file
35
vendor/github.com/Rhymen/go-whatsapp/go.sum
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
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/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
|
||||||
|
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d h1:m3wkrunHupL9XzzM+JZu1pgoDV1d9LFtD0gedNTHVDU=
|
||||||
|
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 h1:muQlzqfZxjptOBjPdv+UoxVMr8Y1rPx7VMGPJIAFc5w=
|
||||||
|
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 h1:xP//3V77YvHd1cj2Z3ttuQWAvs5WmIwBbjKe/t0g/tM=
|
||||||
|
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 h1:IRmRE0SPMByczwE2dhnTcVojje3w2TCSKwFrboLUbDg=
|
||||||
|
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
|
||||||
|
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/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=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
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/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
|
||||||
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) {
|
func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) {
|
||||||
data := []interface{}{"query", "GroupMetadata", jid}
|
data := []interface{}{"query", "GroupMetadata", jid}
|
||||||
return wac.write(data)
|
return wac.writeJson(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) {
|
func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) {
|
||||||
@@ -41,7 +41,7 @@ func (wac *Conn) LeaveGroup(jid string) (<-chan string, error) {
|
|||||||
|
|
||||||
func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
||||||
request := []interface{}{"query", "inviteCode", jid}
|
request := []interface{}{"query", "inviteCode", jid}
|
||||||
ch, err := wac.write(request)
|
ch, err := wac.writeJson(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -63,3 +63,28 @@ func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
|||||||
|
|
||||||
return response["code"].(string), nil
|
return response["code"].(string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) GroupAcceptInviteCode(code string) (jid string, err error) {
|
||||||
|
request := []interface{}{"action", "invite", code}
|
||||||
|
ch, err := wac.writeJson(request)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case r := <-ch:
|
||||||
|
if err := json.Unmarshal([]byte(r), &response); err != nil {
|
||||||
|
return "", fmt.Errorf("error decoding response message: %v\n", err)
|
||||||
|
}
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
return "", fmt.Errorf("request timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(response["status"].(float64)) != 200 {
|
||||||
|
return "", fmt.Errorf("request responded with %d", response["status"])
|
||||||
|
}
|
||||||
|
|
||||||
|
return response["gid"].(string), nil
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@ package whatsapp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matterbridge/go-whatsapp/binary"
|
|
||||||
"github.com/matterbridge/go-whatsapp/binary/proto"
|
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -78,6 +80,22 @@ type RawMessageHandler interface {
|
|||||||
HandleRawMessage(message *proto.WebMessageInfo)
|
HandleRawMessage(message *proto.WebMessageInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The ContactListHandler interface needs to be implemented to applky custom actions to contact lists dispatched by the dispatcher.
|
||||||
|
*/
|
||||||
|
type ContactListHandler interface {
|
||||||
|
Handler
|
||||||
|
HandleContactList(contacts []Contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The ChatListHandler interface needs to be implemented to apply custom actions to chat lists dispatched by the dispatcher.
|
||||||
|
*/
|
||||||
|
type ChatListHandler interface {
|
||||||
|
Handler
|
||||||
|
HandleChatList(contacts []Chat)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
AddHandler adds an handler to the list of handler that receive dispatched messages.
|
AddHandler adds an handler to the list of handler that receive dispatched messages.
|
||||||
The provided handler must at least implement the Handler interface. Additionally implemented
|
The provided handler must at least implement the Handler interface. Additionally implemented
|
||||||
@@ -88,6 +106,27 @@ func (wac *Conn) AddHandler(handler Handler) {
|
|||||||
wac.handler = append(wac.handler, handler)
|
wac.handler = append(wac.handler, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveHandler removes a handler from the list of handlers that receive dispatched messages.
|
||||||
|
func (wac *Conn) RemoveHandler(handler Handler) bool {
|
||||||
|
i := -1
|
||||||
|
for k, v := range wac.handler {
|
||||||
|
if v == handler {
|
||||||
|
i = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i > -1 {
|
||||||
|
wac.handler = append(wac.handler[:i], wac.handler[i+1:]...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveHandlers empties the list of handlers that receive dispatched messages.
|
||||||
|
func (wac *Conn) RemoveHandlers() {
|
||||||
|
wac.handler = make([]Handler, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func (wac *Conn) handle(message interface{}) {
|
func (wac *Conn) handle(message interface{}) {
|
||||||
switch m := message.(type) {
|
switch m := message.(type) {
|
||||||
case error:
|
case error:
|
||||||
@@ -140,6 +179,62 @@ func (wac *Conn) handle(message interface{}) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) handleContacts(contacts interface{}) {
|
||||||
|
var contactList []Contact
|
||||||
|
c, ok := contacts.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, contact := range c {
|
||||||
|
contactNode, ok := contact.(binary.Node)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||||
|
contactList = append(contactList, Contact{
|
||||||
|
jid,
|
||||||
|
contactNode.Attributes["notify"],
|
||||||
|
contactNode.Attributes["name"],
|
||||||
|
contactNode.Attributes["short"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(ContactListHandler); ok {
|
||||||
|
go x.HandleContactList(contactList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) handleChats(chats interface{}) {
|
||||||
|
var chatList []Chat
|
||||||
|
c, ok := chats.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, chat := range c {
|
||||||
|
chatNode, ok := chat.(binary.Node)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||||
|
chatList = append(chatList, Chat{
|
||||||
|
jid,
|
||||||
|
chatNode.Attributes["name"],
|
||||||
|
chatNode.Attributes["count"],
|
||||||
|
chatNode.Attributes["t"],
|
||||||
|
chatNode.Attributes["mute"],
|
||||||
|
chatNode.Attributes["spam"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, h := range wac.handler {
|
||||||
|
if x, ok := h.(ChatListHandler); ok {
|
||||||
|
go x.HandleChatList(chatList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (wac *Conn) dispatch(msg interface{}) {
|
func (wac *Conn) dispatch(msg interface{}) {
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
return
|
return
|
||||||
@@ -158,6 +253,10 @@ func (wac *Conn) dispatch(msg interface{}) {
|
|||||||
}
|
}
|
||||||
} else if message.Description == "response" && message.Attributes["type"] == "contacts" {
|
} else if message.Description == "response" && message.Attributes["type"] == "contacts" {
|
||||||
wac.updateContacts(message.Content)
|
wac.updateContacts(message.Content)
|
||||||
|
wac.handleContacts(message.Content)
|
||||||
|
} else if message.Description == "response" && message.Attributes["type"] == "chat" {
|
||||||
|
wac.updateChats(message.Content)
|
||||||
|
wac.handleChats(message.Content)
|
||||||
}
|
}
|
||||||
case error:
|
case error:
|
||||||
wac.handle(message)
|
wac.handle(message)
|
||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matterbridge/go-whatsapp/crypto/cbc"
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
"github.com/matterbridge/go-whatsapp/crypto/hkdf"
|
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
@@ -133,7 +133,7 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK
|
|||||||
}
|
}
|
||||||
|
|
||||||
uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
|
uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
|
||||||
ch, err := wac.write(uploadReq)
|
ch, err := wac.writeJson(uploadReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, nil, 0, err
|
return "", nil, nil, nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matterbridge/go-whatsapp/binary"
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
"github.com/matterbridge/go-whatsapp/binary/proto"
|
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -22,61 +22,77 @@ const (
|
|||||||
MediaDocument MediaType = "WhatsApp Document Keys"
|
MediaDocument MediaType = "WhatsApp Document Keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (wac *Conn) Send(msg interface{}) error {
|
var msgInfo MessageInfo
|
||||||
|
|
||||||
|
func (wac *Conn) Send(msg interface{}) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
var ch <-chan string
|
var ch <-chan string
|
||||||
|
var msgProto *proto.WebMessageInfo
|
||||||
|
|
||||||
switch m := msg.(type) {
|
switch m := msg.(type) {
|
||||||
case *proto.WebMessageInfo:
|
case *proto.WebMessageInfo:
|
||||||
ch, err = wac.sendProto(m)
|
ch, err = wac.sendProto(m)
|
||||||
case TextMessage:
|
case TextMessage:
|
||||||
ch, err = wac.sendProto(getTextProto(m))
|
msgProto = getTextProto(m)
|
||||||
|
msgInfo = getMessageInfo(msgProto)
|
||||||
|
ch, err = wac.sendProto(msgProto)
|
||||||
case ImageMessage:
|
case ImageMessage:
|
||||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage)
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("image upload failed: %v", err)
|
return "ERROR", fmt.Errorf("image upload failed: %v", err)
|
||||||
}
|
}
|
||||||
ch, err = wac.sendProto(getImageProto(m))
|
msgProto = getImageProto(m)
|
||||||
|
msgInfo = getMessageInfo(msgProto)
|
||||||
|
ch, err = wac.sendProto(msgProto)
|
||||||
case VideoMessage:
|
case VideoMessage:
|
||||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo)
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("video upload failed: %v", err)
|
return "ERROR", fmt.Errorf("video upload failed: %v", err)
|
||||||
}
|
}
|
||||||
ch, err = wac.sendProto(getVideoProto(m))
|
msgProto = getVideoProto(m)
|
||||||
|
msgInfo = getMessageInfo(msgProto)
|
||||||
|
ch, err = wac.sendProto(msgProto)
|
||||||
case DocumentMessage:
|
case DocumentMessage:
|
||||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument)
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("document upload failed: %v", err)
|
return "ERROR", fmt.Errorf("document upload failed: %v", err)
|
||||||
}
|
}
|
||||||
ch, err = wac.sendProto(getDocumentProto(m))
|
msgProto = getDocumentProto(m)
|
||||||
|
msgInfo = getMessageInfo(msgProto)
|
||||||
|
ch, err = wac.sendProto(msgProto)
|
||||||
case AudioMessage:
|
case AudioMessage:
|
||||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio)
|
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("audio upload failed: %v", err)
|
return "ERROR", fmt.Errorf("audio upload failed: %v", err)
|
||||||
}
|
}
|
||||||
ch, err = wac.sendProto(getAudioProto(m))
|
msgProto = getAudioProto(m)
|
||||||
|
msgInfo = getMessageInfo(msgProto)
|
||||||
|
ch, err = wac.sendProto(msgProto)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("cannot match type %T, use message types declared in the package", msg)
|
return "ERROR", fmt.Errorf("cannot match type %T, use message types declared in the package", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not send proto: %v", err)
|
return "ERROR", fmt.Errorf("could not send proto: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case response := <-ch:
|
case response := <-ch:
|
||||||
var resp map[string]interface{}
|
var resp map[string]interface{}
|
||||||
if err = json.Unmarshal([]byte(response), &resp); err != nil {
|
if err = json.Unmarshal([]byte(response), &resp); err != nil {
|
||||||
return fmt.Errorf("error decoding sending response: %v\n", err)
|
return "ERROR", fmt.Errorf("error decoding sending response: %v\n", err)
|
||||||
}
|
}
|
||||||
if int(resp["status"].(float64)) != 200 {
|
if int(resp["status"].(float64)) != 200 {
|
||||||
return fmt.Errorf("message sending responded with %d", resp["status"])
|
return "ERROR", fmt.Errorf("message sending responded with %d", resp["status"])
|
||||||
|
}
|
||||||
|
if int(resp["status"].(float64)) == 200 {
|
||||||
|
return msgInfo.Id, nil
|
||||||
}
|
}
|
||||||
case <-time.After(wac.msgTimeout):
|
case <-time.After(wac.msgTimeout):
|
||||||
return fmt.Errorf("sending message timed out")
|
return "ERROR", fmt.Errorf("sending message timed out")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return "ERROR", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) {
|
func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) {
|
||||||
111
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
Normal file
111
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (wac *Conn) readPump() {
|
||||||
|
defer wac.wg.Done()
|
||||||
|
|
||||||
|
var readErr error
|
||||||
|
var msgType int
|
||||||
|
var reader io.Reader
|
||||||
|
|
||||||
|
for {
|
||||||
|
readerFound := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
msgType, reader, readErr = wac.ws.conn.NextReader()
|
||||||
|
close(readerFound)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-readerFound:
|
||||||
|
if readErr != nil {
|
||||||
|
wac.handle(&ErrConnectionFailed{Err: readErr})
|
||||||
|
_, _ = wac.Disconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
wac.handle(errors.Wrap(err, "error reading message from Reader"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = wac.processReadData(msgType, msg)
|
||||||
|
if err != nil {
|
||||||
|
wac.handle(errors.Wrap(err, "error processing data"))
|
||||||
|
}
|
||||||
|
case <-wac.ws.close:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) processReadData(msgType int, msg []byte) error {
|
||||||
|
data := strings.SplitN(string(msg), ",", 2)
|
||||||
|
|
||||||
|
if data[0][0] == '!' { //Keep-Alive Timestamp
|
||||||
|
data = append(data, data[0][1:]) //data[1]
|
||||||
|
data[0] = "!"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) != 2 || len(data[1]) == 0 {
|
||||||
|
return ErrInvalidWsData
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.listener.RLock()
|
||||||
|
listener, hasListener := wac.listener.m[data[0]]
|
||||||
|
wac.listener.RUnlock()
|
||||||
|
|
||||||
|
if hasListener {
|
||||||
|
// listener only exists for TextMessages query messages out of contact.go
|
||||||
|
// If these binary query messages can be handled another way,
|
||||||
|
// then the TextMessages, which are all JSON encoded, can directly
|
||||||
|
// be unmarshalled. The listener chan could then be changed from type
|
||||||
|
// chan string to something like chan map[string]interface{}. The unmarshalling
|
||||||
|
// in several places, especially in session.go, would then be gone.
|
||||||
|
listener <- data[1]
|
||||||
|
|
||||||
|
wac.listener.Lock()
|
||||||
|
delete(wac.listener.m, data[0])
|
||||||
|
wac.listener.Unlock()
|
||||||
|
} else if msgType == websocket.BinaryMessage && wac.loggedIn {
|
||||||
|
message, err := wac.decryptBinaryMessage([]byte(data[1]))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error decoding binary")
|
||||||
|
}
|
||||||
|
wac.dispatch(message)
|
||||||
|
} else { //RAW json status updates
|
||||||
|
wac.handle(string(data[1]))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
|
||||||
|
//message validation
|
||||||
|
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
||||||
|
h2.Write([]byte(msg[32:]))
|
||||||
|
if !hmac.Equal(h2.Sum(nil), msg[:32]) {
|
||||||
|
return nil, ErrInvalidHmac
|
||||||
|
}
|
||||||
|
|
||||||
|
// message decrypt
|
||||||
|
d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "decrypting message with AES-CBC failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// message unmarshal
|
||||||
|
message, err := binary.Unmarshal(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not decode binary")
|
||||||
|
}
|
||||||
|
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
@@ -7,16 +7,20 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matterbridge/go-whatsapp/crypto/cbc"
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
"github.com/matterbridge/go-whatsapp/crypto/curve25519"
|
"github.com/Rhymen/go-whatsapp/crypto/curve25519"
|
||||||
"github.com/matterbridge/go-whatsapp/crypto/hkdf"
|
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//represents the WhatsAppWeb client version
|
||||||
|
var waVersion = []int{0, 3, 3324}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Session contains session individual information. To be able to resume the connection without scanning the qr code
|
Session contains session individual information. To be able to resume the connection without scanning the qr code
|
||||||
every time you should save the Session returned by Login and use RestoreSession the next time you want to login.
|
every time you should save the Session returned by Login and use RestoreWithSession the next time you want to login.
|
||||||
Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after
|
Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after
|
||||||
every re-login and should be saved every time.
|
every re-login and should be saved every time.
|
||||||
*/
|
*/
|
||||||
@@ -98,7 +102,7 @@ func (wac *Conn) SetClientName(long, short string) error {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
|
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
|
||||||
every time, you should save the returned session and use RestoreSession the next time. Login takes a writable channel
|
every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel
|
||||||
as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data
|
as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data
|
||||||
should be displayed as an qr code in a way you prefer. To print a qr code to console you can use:
|
should be displayed as an qr code in a way you prefer. To print a qr code to console you can use:
|
||||||
github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
||||||
@@ -121,7 +125,21 @@ github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
|||||||
*/
|
*/
|
||||||
func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
||||||
session := Session{}
|
session := Session{}
|
||||||
|
//Makes sure that only a single Login or Restore can happen at the same time
|
||||||
|
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
||||||
|
return session, ErrLoginInProgress
|
||||||
|
}
|
||||||
|
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
||||||
|
|
||||||
|
if wac.loggedIn {
|
||||||
|
return session, ErrAlreadyLoggedIn
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//logged in?!?
|
||||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
||||||
return session, fmt.Errorf("already logged in")
|
return session, fmt.Errorf("already logged in")
|
||||||
}
|
}
|
||||||
@@ -133,9 +151,8 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
session.ClientId = base64.StdEncoding.EncodeToString(clientId)
|
session.ClientId = base64.StdEncoding.EncodeToString(clientId)
|
||||||
//oldVersion=8691
|
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
||||||
login := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
loginChan, err := wac.writeJson(login)
|
||||||
loginChan, err := wac.write(login)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return session, fmt.Errorf("error writing login: %v\n", err)
|
return session, fmt.Errorf("error writing login: %v\n", err)
|
||||||
}
|
}
|
||||||
@@ -160,14 +177,16 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//listener for Login response
|
//listener for Login response
|
||||||
messageTag := "s1"
|
s1 := make(chan string, 1)
|
||||||
wac.listener[messageTag] = make(chan string, 1)
|
wac.listener.Lock()
|
||||||
|
wac.listener.m["s1"] = s1
|
||||||
|
wac.listener.Unlock()
|
||||||
|
|
||||||
qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId)
|
qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId)
|
||||||
|
|
||||||
var resp2 []interface{}
|
var resp2 []interface{}
|
||||||
select {
|
select {
|
||||||
case r1 := <-wac.listener[messageTag]:
|
case r1 := <-s1:
|
||||||
if err := json.Unmarshal([]byte(r1), &resp2); err != nil {
|
if err := json.Unmarshal([]byte(r1), &resp2); err != nil {
|
||||||
return session, fmt.Errorf("error decoding qr code resp: %v", err)
|
return session, fmt.Errorf("error decoding qr code resp: %v", err)
|
||||||
}
|
}
|
||||||
@@ -226,90 +245,136 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
|||||||
session.EncKey = keyDecrypted[:32]
|
session.EncKey = keyDecrypted[:32]
|
||||||
session.MacKey = keyDecrypted[32:64]
|
session.MacKey = keyDecrypted[32:64]
|
||||||
wac.session = &session
|
wac.session = &session
|
||||||
|
wac.loggedIn = true
|
||||||
|
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: GoDoc
|
||||||
/*
|
/*
|
||||||
RestoreSession is the function that restores a given session. It will try to reestablish the connection to the
|
Basically the old RestoreSession functionality
|
||||||
|
*/
|
||||||
|
func (wac *Conn) RestoreWithSession(session Session) (_ Session, err error) {
|
||||||
|
if wac.loggedIn {
|
||||||
|
return Session{}, ErrAlreadyLoggedIn
|
||||||
|
}
|
||||||
|
old := wac.session
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
wac.session = old
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wac.session = &session
|
||||||
|
|
||||||
|
if err = wac.Restore(); err != nil {
|
||||||
|
wac.session = nil
|
||||||
|
return Session{}, err
|
||||||
|
}
|
||||||
|
return *wac.session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//TODO: GoDoc
|
||||||
|
RestoreWithSession is the function that restores a given session. It will try to reestablish the connection to the
|
||||||
WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be
|
WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be
|
||||||
saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not
|
saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not
|
||||||
suggested. If so, a challenge has to be resolved which is just another possible point of failure.
|
suggested. If so, a challenge has to be resolved which is just another possible point of failure.
|
||||||
*/
|
*/
|
||||||
func (wac *Conn) RestoreSession(session Session) (Session, error) {
|
func (wac *Conn) Restore() error {
|
||||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
//Makes sure that only a single Login or Restore can happen at the same time
|
||||||
return Session{}, fmt.Errorf("already logged in")
|
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
||||||
|
return ErrLoginInProgress
|
||||||
|
}
|
||||||
|
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
||||||
|
|
||||||
|
if wac.session == nil {
|
||||||
|
return ErrInvalidSession
|
||||||
}
|
}
|
||||||
|
|
||||||
wac.session = &session
|
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if wac.loggedIn {
|
||||||
|
return ErrAlreadyLoggedIn
|
||||||
|
}
|
||||||
|
|
||||||
//listener for Conn or challenge; s1 is not allowed to drop
|
//listener for Conn or challenge; s1 is not allowed to drop
|
||||||
wac.listener["s1"] = make(chan string, 1)
|
s1 := make(chan string, 1)
|
||||||
|
wac.listener.Lock()
|
||||||
|
wac.listener.m["s1"] = s1
|
||||||
|
wac.listener.Unlock()
|
||||||
|
|
||||||
//admin init
|
//admin init
|
||||||
init := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true}
|
||||||
initChan, err := wac.write(init)
|
initChan, err := wac.writeJson(init)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error writing admin init: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error writing admin init: %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//admin login with takeover
|
//admin login with takeover
|
||||||
login := []interface{}{"admin", "login", session.ClientToken, session.ServerToken, session.ClientId, "takeover"}
|
login := []interface{}{"admin", "login", wac.session.ClientToken, wac.session.ServerToken, wac.session.ClientId, "takeover"}
|
||||||
loginChan, err := wac.write(login)
|
loginChan, err := wac.writeJson(login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error writing admin login: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error writing admin login: %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case r := <-initChan:
|
case r := <-initChan:
|
||||||
var resp map[string]interface{}
|
var resp map[string]interface{}
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(resp["status"].(float64)) != 200 {
|
if int(resp["status"].(float64)) != 200 {
|
||||||
wac.session = nil
|
return fmt.Errorf("init responded with %d", resp["status"])
|
||||||
return Session{}, fmt.Errorf("init responded with %d", resp["status"])
|
|
||||||
}
|
}
|
||||||
case <-time.After(wac.msgTimeout):
|
case <-time.After(wac.msgTimeout):
|
||||||
wac.session = nil
|
return fmt.Errorf("restore session init timed out")
|
||||||
return Session{}, fmt.Errorf("restore session init timed out")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//wait for s1
|
//wait for s1
|
||||||
var connResp []interface{}
|
var connResp []interface{}
|
||||||
select {
|
select {
|
||||||
case r1 := <-wac.listener["s1"]:
|
case r1 := <-s1:
|
||||||
if err := json.Unmarshal([]byte(r1), &connResp); err != nil {
|
if err := json.Unmarshal([]byte(r1), &connResp); err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error decoding s1 message: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error decoding s1 message: %v\n", err)
|
|
||||||
}
|
}
|
||||||
case <-time.After(wac.msgTimeout):
|
case <-time.After(wac.msgTimeout):
|
||||||
wac.session = nil
|
|
||||||
return Session{}, fmt.Errorf("restore session connection timed out")
|
//check for an error message
|
||||||
|
select {
|
||||||
|
case r := <-loginChan:
|
||||||
|
var resp map[string]interface{}
|
||||||
|
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||||
|
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||||
|
}
|
||||||
|
if int(resp["status"].(float64)) != 200 {
|
||||||
|
return fmt.Errorf("admin login responded with %d", int(resp["status"].(float64)))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// not even an error message – assume timeout
|
||||||
|
return fmt.Errorf("restore session connection timed out")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if challenge is present
|
//check if challenge is present
|
||||||
if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" {
|
if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" {
|
||||||
wac.listener["s2"] = make(chan string, 1)
|
s2 := make(chan string, 1)
|
||||||
|
wac.listener.Lock()
|
||||||
|
wac.listener.m["s2"] = s2
|
||||||
|
wac.listener.Unlock()
|
||||||
|
|
||||||
if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil {
|
if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error resolving challenge: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error resolving challenge: %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case r := <-wac.listener["s2"]:
|
case r := <-s2:
|
||||||
if err := json.Unmarshal([]byte(r), &connResp); err != nil {
|
if err := json.Unmarshal([]byte(r), &connResp); err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error decoding s2 message: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error decoding s2 message: %v\n", err)
|
|
||||||
}
|
}
|
||||||
case <-time.After(wac.msgTimeout):
|
case <-time.After(wac.msgTimeout):
|
||||||
wac.session = nil
|
return fmt.Errorf("restore session challenge timed out")
|
||||||
return Session{}, fmt.Errorf("restore session challenge timed out")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,17 +383,14 @@ func (wac *Conn) RestoreSession(session Session) (Session, error) {
|
|||||||
case r := <-loginChan:
|
case r := <-loginChan:
|
||||||
var resp map[string]interface{}
|
var resp map[string]interface{}
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||||
wac.session = nil
|
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||||
return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(resp["status"].(float64)) != 200 {
|
if int(resp["status"].(float64)) != 200 {
|
||||||
wac.session = nil
|
return fmt.Errorf("admin login responded with %d", resp["status"])
|
||||||
return Session{}, fmt.Errorf("admin login responded with %d", resp["status"])
|
|
||||||
}
|
}
|
||||||
case <-time.After(wac.msgTimeout):
|
case <-time.After(wac.msgTimeout):
|
||||||
wac.session = nil
|
return fmt.Errorf("restore session login timed out")
|
||||||
return Session{}, fmt.Errorf("restore session login timed out")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info := connResp[1].(map[string]interface{})
|
info := connResp[1].(map[string]interface{})
|
||||||
@@ -336,11 +398,12 @@ func (wac *Conn) RestoreSession(session Session) (Session, error) {
|
|||||||
wac.Info = newInfoFromReq(info)
|
wac.Info = newInfoFromReq(info)
|
||||||
|
|
||||||
//set new tokens
|
//set new tokens
|
||||||
session.ClientToken = info["clientToken"].(string)
|
wac.session.ClientToken = info["clientToken"].(string)
|
||||||
session.ServerToken = info["serverToken"].(string)
|
wac.session.ServerToken = info["serverToken"].(string)
|
||||||
session.Wid = info["wid"].(string)
|
wac.session.Wid = info["wid"].(string)
|
||||||
|
wac.loggedIn = true
|
||||||
|
|
||||||
return *wac.session, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wac *Conn) resolveChallenge(challenge string) error {
|
func (wac *Conn) resolveChallenge(challenge string) error {
|
||||||
@@ -353,7 +416,7 @@ func (wac *Conn) resolveChallenge(challenge string) error {
|
|||||||
h2.Write([]byte(decoded))
|
h2.Write([]byte(decoded))
|
||||||
|
|
||||||
ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId}
|
ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId}
|
||||||
challengeChan, err := wac.write(ch)
|
challengeChan, err := wac.writeJson(ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error writing challenge: %v\n", err)
|
return fmt.Errorf("error writing challenge: %v\n", err)
|
||||||
}
|
}
|
||||||
@@ -380,7 +443,7 @@ The session can not be resumed and will disappear on your phone in the WhatsAppW
|
|||||||
*/
|
*/
|
||||||
func (wac *Conn) Logout() error {
|
func (wac *Conn) Logout() error {
|
||||||
login := []interface{}{"admin", "Conn", "disconnect"}
|
login := []interface{}{"admin", "Conn", "disconnect"}
|
||||||
_, err := wac.write(login)
|
_, err := wac.writeJson(login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error writing logout: %v\n", err)
|
return fmt.Errorf("error writing logout: %v\n", err)
|
||||||
}
|
}
|
||||||
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
Normal file
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store struct {
|
||||||
|
Contacts map[string]Contact
|
||||||
|
Chats map[string]Chat
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
Jid string
|
||||||
|
Notify string
|
||||||
|
Name string
|
||||||
|
Short string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Chat struct {
|
||||||
|
Jid string
|
||||||
|
Name string
|
||||||
|
Unread string
|
||||||
|
LastMessageTime string
|
||||||
|
IsMuted string
|
||||||
|
IsMarkedSpam string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStore() *Store {
|
||||||
|
return &Store{
|
||||||
|
make(map[string]Contact),
|
||||||
|
make(map[string]Chat),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) updateContacts(contacts interface{}) {
|
||||||
|
c, ok := contacts.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, contact := range c {
|
||||||
|
contactNode, ok := contact.(binary.Node)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||||
|
wac.Store.Contacts[jid] = Contact{
|
||||||
|
jid,
|
||||||
|
contactNode.Attributes["notify"],
|
||||||
|
contactNode.Attributes["name"],
|
||||||
|
contactNode.Attributes["short"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) updateChats(chats interface{}) {
|
||||||
|
c, ok := chats.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, chat := range c {
|
||||||
|
chatNode, ok := chat.(binary.Node)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||||
|
wac.Store.Chats[jid] = Chat{
|
||||||
|
jid,
|
||||||
|
chatNode.Attributes["name"],
|
||||||
|
chatNode.Attributes["count"],
|
||||||
|
chatNode.Attributes["t"],
|
||||||
|
chatNode.Attributes["mute"],
|
||||||
|
chatNode.Attributes["spam"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
125
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
Normal file
125
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Rhymen/go-whatsapp/binary"
|
||||||
|
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//writeJson enqueues a json message into the writeChan
|
||||||
|
func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
|
||||||
|
d, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := time.Now().Unix()
|
||||||
|
messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||||
|
bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d))
|
||||||
|
|
||||||
|
ch, err := wac.write(websocket.TextMessage, messageTag, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.msgCount++
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) {
|
||||||
|
if len(messageTag) < 2 {
|
||||||
|
return nil, ErrMissingMessageTag
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := wac.encryptBinaryMessage(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "encryptBinaryMessage(node) failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := []byte(messageTag + ",")
|
||||||
|
bytes = append(bytes, byte(metric), byte(flag))
|
||||||
|
bytes = append(bytes, data...)
|
||||||
|
|
||||||
|
ch, err := wac.write(websocket.BinaryMessage, messageTag, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to write message")
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.msgCount++
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) sendKeepAlive() error {
|
||||||
|
bytes := []byte("?,,")
|
||||||
|
respChan, err := wac.write(websocket.TextMessage, "!", bytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error sending keepAlive")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case resp := <-respChan:
|
||||||
|
msecs, err := strconv.ParseInt(resp, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Error converting time string to uint")
|
||||||
|
}
|
||||||
|
wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
|
||||||
|
|
||||||
|
case <-time.After(wac.msgTimeout):
|
||||||
|
return ErrConnectionTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) {
|
||||||
|
var ch chan string
|
||||||
|
if answerMessageTag != "" {
|
||||||
|
ch = make(chan string, 1)
|
||||||
|
|
||||||
|
wac.listener.Lock()
|
||||||
|
wac.listener.m[answerMessageTag] = ch
|
||||||
|
wac.listener.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
wac.ws.Lock()
|
||||||
|
err := wac.ws.conn.WriteMessage(messageType, data)
|
||||||
|
wac.ws.Unlock()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if answerMessageTag != "" {
|
||||||
|
wac.listener.Lock()
|
||||||
|
delete(wac.listener.m, answerMessageTag)
|
||||||
|
wac.listener.Unlock()
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "error writing to websocket")
|
||||||
|
}
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) {
|
||||||
|
b, err := binary.Marshal(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "binary node marshal failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "encrypt failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, wac.session.MacKey)
|
||||||
|
h.Write(cipher)
|
||||||
|
hash := h.Sum(nil)
|
||||||
|
|
||||||
|
data = append(data, hash[:32]...)
|
||||||
|
data = append(data, cipher...)
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
5
vendor/github.com/d5/tengo/Makefile
generated
vendored
5
vendor/github.com/d5/tengo/Makefile
generated
vendored
@@ -1,10 +1,13 @@
|
|||||||
vet:
|
vet:
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|
||||||
|
generate:
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
golint -set_exit_status ./...
|
golint -set_exit_status ./...
|
||||||
|
|
||||||
test: vet lint
|
test: generate vet lint
|
||||||
go test -race -cover ./...
|
go test -race -cover ./...
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
|
|||||||
32
vendor/github.com/d5/tengo/README.md
generated
vendored
32
vendor/github.com/d5/tengo/README.md
generated
vendored
@@ -7,7 +7,6 @@
|
|||||||
[](https://godoc.org/github.com/d5/tengo/script)
|
[](https://godoc.org/github.com/d5/tengo/script)
|
||||||
[](https://goreportcard.com/report/github.com/d5/tengo)
|
[](https://goreportcard.com/report/github.com/d5/tengo)
|
||||||
[](https://travis-ci.org/d5/tengo)
|
[](https://travis-ci.org/d5/tengo)
|
||||||
[](https://www.patreon.com/tengolang)
|
|
||||||
|
|
||||||
**Tengo is a small, dynamic, fast, secure script language for Go.**
|
**Tengo is a small, dynamic, fast, secure script language for Go.**
|
||||||
|
|
||||||
@@ -16,6 +15,8 @@ Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as byt
|
|||||||
```golang
|
```golang
|
||||||
/* The Tengo Language */
|
/* The Tengo Language */
|
||||||
|
|
||||||
|
fmt := import("fmt")
|
||||||
|
|
||||||
each := func(seq, fn) {
|
each := func(seq, fn) {
|
||||||
for x in seq { fn(x) }
|
for x in seq { fn(x) }
|
||||||
}
|
}
|
||||||
@@ -25,11 +26,11 @@ sum := func(init, seq) {
|
|||||||
return init
|
return init
|
||||||
}
|
}
|
||||||
|
|
||||||
n := sum(0, [1, 2, 3]) // == 6
|
fmt.println(sum(0, [1, 2, 3])) // "6"
|
||||||
s := sum("", [1, 2, 3]) // == "123"
|
fmt.println(sum("", [1, 2, 3])) // "123"
|
||||||
```
|
```
|
||||||
|
|
||||||
> Run this code in the [Playground](https://tengolang.com/?s=d01cf9ed81daba939e26618530eb171f7397d9c9)
|
> Run this code in the [Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -41,22 +42,23 @@ s := sum("", [1, 2, 3]) // == "123"
|
|||||||
- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md)
|
- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md)
|
||||||
- Compiler/runtime written in native Go _(no external deps or cgo)_
|
- Compiler/runtime written in native Go _(no external deps or cgo)_
|
||||||
- Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL
|
- Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL
|
||||||
|
- Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), [gaming](https://github.com/d5/pbr), data pipeline, [transpiler](https://github.com/d5/tengo2lua)
|
||||||
|
|
||||||
## Benchmark
|
## Benchmark
|
||||||
|
|
||||||
| | fib(35) | fibt(35) | Type |
|
| | fib(35) | fibt(35) | Type |
|
||||||
| :--- | ---: | ---: | :---: |
|
| :--- | ---: | ---: | :---: |
|
||||||
| Go | `58ms` | `4ms` | Go (native) |
|
| Go | `48ms` | `3ms` | Go (native) |
|
||||||
| [**Tengo**](https://github.com/d5/tengo) | `4,180ms` | `5ms` | VM on Go |
|
| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go |
|
||||||
| Lua | `1,695ms` | `3ms` | Lua (native) |
|
| Lua | `1,416ms` | `3ms` | Lua (native) |
|
||||||
| [go-lua](https://github.com/Shopify/go-lua) | `5,163ms` | `5ms` | Lua VM on Go |
|
| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go |
|
||||||
| [GopherLua](https://github.com/yuin/gopher-lua) | `5,525ms` | `5ms` | Lua VM on Go |
|
| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go |
|
||||||
| Python | `3,097ms` | `27ms` | Python (native) |
|
| Python | `2,588ms` | `26ms` | Python (native) |
|
||||||
| [starlark-go](https://github.com/google/starlark-go) | `15,307ms` | `5ms` | Python-like Interpreter on Go |
|
| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go |
|
||||||
| [gpython](https://github.com/go-python/gpython) | `17,656ms` | `5ms` | Python Interpreter on Go |
|
| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go |
|
||||||
| [goja](https://github.com/dop251/goja) | `6,876ms` | `5ms` | JS VM on Go |
|
| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go |
|
||||||
| [otto](https://github.com/robertkrimen/otto) | `81,886ms` | `12ms` | JS Interpreter on Go |
|
| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go |
|
||||||
| [Anko](https://github.com/mattn/anko) | `97,517ms` | `14ms` | Interpreter on Go |
|
| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go |
|
||||||
|
|
||||||
_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_
|
_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_
|
||||||
_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_
|
_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_
|
||||||
|
|||||||
66
vendor/github.com/d5/tengo/compiler/bytecode.go
generated
vendored
66
vendor/github.com/d5/tengo/compiler/bytecode.go
generated
vendored
@@ -17,32 +17,6 @@ type Bytecode struct {
|
|||||||
Constants []objects.Object
|
Constants []objects.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode reads Bytecode data from the reader.
|
|
||||||
func (b *Bytecode) Decode(r io.Reader) error {
|
|
||||||
dec := gob.NewDecoder(r)
|
|
||||||
|
|
||||||
if err := dec.Decode(&b.FileSet); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet
|
|
||||||
// as it's private field and not serialized by gob encoder/decoder.
|
|
||||||
|
|
||||||
if err := dec.Decode(&b.MainFunction); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dec.Decode(&b.Constants); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace Bool and Undefined with known value
|
|
||||||
for i, v := range b.Constants {
|
|
||||||
b.Constants[i] = cleanupObjects(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes Bytecode data to the writer.
|
// Encode writes Bytecode data to the writer.
|
||||||
func (b *Bytecode) Encode(w io.Writer) error {
|
func (b *Bytecode) Encode(w io.Writer) error {
|
||||||
enc := gob.NewEncoder(w)
|
enc := gob.NewEncoder(w)
|
||||||
@@ -59,6 +33,17 @@ func (b *Bytecode) Encode(w io.Writer) error {
|
|||||||
return enc.Encode(b.Constants)
|
return enc.Encode(b.Constants)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountObjects returns the number of objects found in Constants.
|
||||||
|
func (b *Bytecode) CountObjects() int {
|
||||||
|
n := 0
|
||||||
|
|
||||||
|
for _, c := range b.Constants {
|
||||||
|
n += objects.CountObjects(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
// FormatInstructions returns human readable string representations of
|
// FormatInstructions returns human readable string representations of
|
||||||
// compiled instructions.
|
// compiled instructions.
|
||||||
func (b *Bytecode) FormatInstructions() []string {
|
func (b *Bytecode) FormatInstructions() []string {
|
||||||
@@ -83,51 +68,22 @@ func (b *Bytecode) FormatConstants() (output []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanupObjects(o objects.Object) objects.Object {
|
|
||||||
switch o := o.(type) {
|
|
||||||
case *objects.Bool:
|
|
||||||
if o.IsFalsy() {
|
|
||||||
return objects.FalseValue
|
|
||||||
}
|
|
||||||
return objects.TrueValue
|
|
||||||
case *objects.Undefined:
|
|
||||||
return objects.UndefinedValue
|
|
||||||
case *objects.Array:
|
|
||||||
for i, v := range o.Value {
|
|
||||||
o.Value[i] = cleanupObjects(v)
|
|
||||||
}
|
|
||||||
case *objects.Map:
|
|
||||||
for k, v := range o.Value {
|
|
||||||
o.Value[k] = cleanupObjects(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
gob.Register(&source.FileSet{})
|
gob.Register(&source.FileSet{})
|
||||||
gob.Register(&source.File{})
|
gob.Register(&source.File{})
|
||||||
gob.Register(&objects.Array{})
|
gob.Register(&objects.Array{})
|
||||||
gob.Register(&objects.ArrayIterator{})
|
|
||||||
gob.Register(&objects.Bool{})
|
gob.Register(&objects.Bool{})
|
||||||
gob.Register(&objects.Break{})
|
|
||||||
gob.Register(&objects.BuiltinFunction{})
|
|
||||||
gob.Register(&objects.Bytes{})
|
gob.Register(&objects.Bytes{})
|
||||||
gob.Register(&objects.Char{})
|
gob.Register(&objects.Char{})
|
||||||
gob.Register(&objects.Closure{})
|
gob.Register(&objects.Closure{})
|
||||||
gob.Register(&objects.CompiledFunction{})
|
gob.Register(&objects.CompiledFunction{})
|
||||||
gob.Register(&objects.Continue{})
|
|
||||||
gob.Register(&objects.Error{})
|
gob.Register(&objects.Error{})
|
||||||
gob.Register(&objects.Float{})
|
gob.Register(&objects.Float{})
|
||||||
gob.Register(&objects.ImmutableArray{})
|
gob.Register(&objects.ImmutableArray{})
|
||||||
gob.Register(&objects.ImmutableMap{})
|
gob.Register(&objects.ImmutableMap{})
|
||||||
gob.Register(&objects.Int{})
|
gob.Register(&objects.Int{})
|
||||||
gob.Register(&objects.Map{})
|
gob.Register(&objects.Map{})
|
||||||
gob.Register(&objects.MapIterator{})
|
|
||||||
gob.Register(&objects.ReturnValue{})
|
|
||||||
gob.Register(&objects.String{})
|
gob.Register(&objects.String{})
|
||||||
gob.Register(&objects.StringIterator{})
|
|
||||||
gob.Register(&objects.Time{})
|
gob.Register(&objects.Time{})
|
||||||
gob.Register(&objects.Undefined{})
|
gob.Register(&objects.Undefined{})
|
||||||
gob.Register(&objects.UserFunction{})
|
gob.Register(&objects.UserFunction{})
|
||||||
|
|||||||
97
vendor/github.com/d5/tengo/compiler/bytecode_decode.go
generated
vendored
Normal file
97
vendor/github.com/d5/tengo/compiler/bytecode_decode.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode reads Bytecode data from the reader.
|
||||||
|
func (b *Bytecode) Decode(r io.Reader, modules *objects.ModuleMap) error {
|
||||||
|
if modules == nil {
|
||||||
|
modules = objects.NewModuleMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := gob.NewDecoder(r)
|
||||||
|
|
||||||
|
if err := dec.Decode(&b.FileSet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet
|
||||||
|
// as it's private field and not serialized by gob encoder/decoder.
|
||||||
|
|
||||||
|
if err := dec.Decode(&b.MainFunction); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dec.Decode(&b.Constants); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, v := range b.Constants {
|
||||||
|
fv, err := fixDecoded(v, modules)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.Constants[i] = fv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixDecoded(o objects.Object, modules *objects.ModuleMap) (objects.Object, error) {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *objects.Bool:
|
||||||
|
if o.IsFalsy() {
|
||||||
|
return objects.FalseValue, nil
|
||||||
|
}
|
||||||
|
return objects.TrueValue, nil
|
||||||
|
case *objects.Undefined:
|
||||||
|
return objects.UndefinedValue, nil
|
||||||
|
case *objects.Array:
|
||||||
|
for i, v := range o.Value {
|
||||||
|
fv, err := fixDecoded(v, modules)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o.Value[i] = fv
|
||||||
|
}
|
||||||
|
case *objects.ImmutableArray:
|
||||||
|
for i, v := range o.Value {
|
||||||
|
fv, err := fixDecoded(v, modules)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o.Value[i] = fv
|
||||||
|
}
|
||||||
|
case *objects.Map:
|
||||||
|
for k, v := range o.Value {
|
||||||
|
fv, err := fixDecoded(v, modules)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o.Value[k] = fv
|
||||||
|
}
|
||||||
|
case *objects.ImmutableMap:
|
||||||
|
modName := moduleName(o)
|
||||||
|
if mod := modules.GetBuiltinModule(modName); mod != nil {
|
||||||
|
return mod.AsImmutableMap(modName), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range o.Value {
|
||||||
|
// encoding of user function not supported
|
||||||
|
if _, isUserFunction := v.(*objects.UserFunction); isUserFunction {
|
||||||
|
return nil, fmt.Errorf("user function not decodable")
|
||||||
|
}
|
||||||
|
|
||||||
|
fv, err := fixDecoded(v, modules)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o.Value[k] = fv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
129
vendor/github.com/d5/tengo/compiler/bytecode_optimize.go
generated
vendored
Normal file
129
vendor/github.com/d5/tengo/compiler/bytecode_optimize.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemoveDuplicates finds and remove the duplicate values in Constants.
|
||||||
|
// Note this function mutates Bytecode.
|
||||||
|
func (b *Bytecode) RemoveDuplicates() {
|
||||||
|
var deduped []objects.Object
|
||||||
|
|
||||||
|
indexMap := make(map[int]int) // mapping from old constant index to new index
|
||||||
|
ints := make(map[int64]int)
|
||||||
|
strings := make(map[string]int)
|
||||||
|
floats := make(map[float64]int)
|
||||||
|
chars := make(map[rune]int)
|
||||||
|
immutableMaps := make(map[string]int) // for modules
|
||||||
|
|
||||||
|
for curIdx, c := range b.Constants {
|
||||||
|
switch c := c.(type) {
|
||||||
|
case *objects.CompiledFunction:
|
||||||
|
// add to deduped list
|
||||||
|
indexMap[curIdx] = len(deduped)
|
||||||
|
deduped = append(deduped, c)
|
||||||
|
case *objects.ImmutableMap:
|
||||||
|
modName := moduleName(c)
|
||||||
|
newIdx, ok := immutableMaps[modName]
|
||||||
|
if modName != "" && ok {
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
} else {
|
||||||
|
newIdx = len(deduped)
|
||||||
|
immutableMaps[modName] = newIdx
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
deduped = append(deduped, c)
|
||||||
|
}
|
||||||
|
case *objects.Int:
|
||||||
|
if newIdx, ok := ints[c.Value]; ok {
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
} else {
|
||||||
|
newIdx = len(deduped)
|
||||||
|
ints[c.Value] = newIdx
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
deduped = append(deduped, c)
|
||||||
|
}
|
||||||
|
case *objects.String:
|
||||||
|
if newIdx, ok := strings[c.Value]; ok {
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
} else {
|
||||||
|
newIdx = len(deduped)
|
||||||
|
strings[c.Value] = newIdx
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
deduped = append(deduped, c)
|
||||||
|
}
|
||||||
|
case *objects.Float:
|
||||||
|
if newIdx, ok := floats[c.Value]; ok {
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
} else {
|
||||||
|
newIdx = len(deduped)
|
||||||
|
floats[c.Value] = newIdx
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
deduped = append(deduped, c)
|
||||||
|
}
|
||||||
|
case *objects.Char:
|
||||||
|
if newIdx, ok := chars[c.Value]; ok {
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
} else {
|
||||||
|
newIdx = len(deduped)
|
||||||
|
chars[c.Value] = newIdx
|
||||||
|
indexMap[curIdx] = newIdx
|
||||||
|
deduped = append(deduped, c)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace with de-duplicated constants
|
||||||
|
b.Constants = deduped
|
||||||
|
|
||||||
|
// update CONST instructions with new indexes
|
||||||
|
// main function
|
||||||
|
updateConstIndexes(b.MainFunction.Instructions, indexMap)
|
||||||
|
// other compiled functions in constants
|
||||||
|
for _, c := range b.Constants {
|
||||||
|
switch c := c.(type) {
|
||||||
|
case *objects.CompiledFunction:
|
||||||
|
updateConstIndexes(c.Instructions, indexMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateConstIndexes(insts []byte, indexMap map[int]int) {
|
||||||
|
i := 0
|
||||||
|
for i < len(insts) {
|
||||||
|
op := insts[i]
|
||||||
|
numOperands := OpcodeOperands[op]
|
||||||
|
_, read := ReadOperands(numOperands, insts[i+1:])
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case OpConstant:
|
||||||
|
curIdx := int(insts[i+2]) | int(insts[i+1])<<8
|
||||||
|
newIdx, ok := indexMap[curIdx]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("constant index not found: %d", curIdx))
|
||||||
|
}
|
||||||
|
copy(insts[i:], MakeInstruction(op, newIdx))
|
||||||
|
case OpClosure:
|
||||||
|
curIdx := int(insts[i+2]) | int(insts[i+1])<<8
|
||||||
|
numFree := int(insts[i+3])
|
||||||
|
newIdx, ok := indexMap[curIdx]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("constant index not found: %d", curIdx))
|
||||||
|
}
|
||||||
|
copy(insts[i:], MakeInstruction(op, newIdx, numFree))
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1 + read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func moduleName(mod *objects.ImmutableMap) string {
|
||||||
|
if modName, ok := mod.Value["__module_name__"].(*objects.String); ok {
|
||||||
|
return modName.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
7
vendor/github.com/d5/tengo/compiler/compilation_scope.go
generated
vendored
7
vendor/github.com/d5/tengo/compiler/compilation_scope.go
generated
vendored
@@ -5,8 +5,7 @@ import "github.com/d5/tengo/compiler/source"
|
|||||||
// CompilationScope represents a compiled instructions
|
// CompilationScope represents a compiled instructions
|
||||||
// and the last two instructions that were emitted.
|
// and the last two instructions that were emitted.
|
||||||
type CompilationScope struct {
|
type CompilationScope struct {
|
||||||
instructions []byte
|
instructions []byte
|
||||||
lastInstructions [2]EmittedInstruction
|
symbolInit map[string]bool
|
||||||
symbolInit map[string]bool
|
sourceMap map[int]source.Pos
|
||||||
sourceMap map[int]source.Pos
|
|
||||||
}
|
}
|
||||||
|
|||||||
270
vendor/github.com/d5/tengo/compiler/compiler.go
generated
vendored
270
vendor/github.com/d5/tengo/compiler/compiler.go
generated
vendored
@@ -3,7 +3,10 @@ package compiler
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/d5/tengo"
|
"github.com/d5/tengo"
|
||||||
"github.com/d5/tengo/compiler/ast"
|
"github.com/d5/tengo/compiler/ast"
|
||||||
@@ -16,14 +19,14 @@ import (
|
|||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
file *source.File
|
file *source.File
|
||||||
parent *Compiler
|
parent *Compiler
|
||||||
moduleName string
|
modulePath string
|
||||||
constants []objects.Object
|
constants []objects.Object
|
||||||
symbolTable *SymbolTable
|
symbolTable *SymbolTable
|
||||||
scopes []CompilationScope
|
scopes []CompilationScope
|
||||||
scopeIndex int
|
scopeIndex int
|
||||||
moduleLoader ModuleLoader
|
modules *objects.ModuleMap
|
||||||
builtinModules map[string]bool
|
|
||||||
compiledModules map[string]*objects.CompiledFunction
|
compiledModules map[string]*objects.CompiledFunction
|
||||||
|
allowFileImport bool
|
||||||
loops []*Loop
|
loops []*Loop
|
||||||
loopIndex int
|
loopIndex int
|
||||||
trace io.Writer
|
trace io.Writer
|
||||||
@@ -31,12 +34,7 @@ type Compiler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewCompiler creates a Compiler.
|
// NewCompiler creates a Compiler.
|
||||||
// User can optionally provide the symbol table if one wants to add or remove
|
func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, modules *objects.ModuleMap, trace io.Writer) *Compiler {
|
||||||
// some global- or builtin- scope symbols. If not (nil), Compile will create
|
|
||||||
// a new symbol table and use the default builtin functions. Likewise, standard
|
|
||||||
// modules can be explicitly provided if user wants to add or remove some modules.
|
|
||||||
// By default, Compile will use all the standard modules otherwise.
|
|
||||||
func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, builtinModules map[string]bool, trace io.Writer) *Compiler {
|
|
||||||
mainScope := CompilationScope{
|
mainScope := CompilationScope{
|
||||||
symbolInit: make(map[string]bool),
|
symbolInit: make(map[string]bool),
|
||||||
sourceMap: make(map[int]source.Pos),
|
sourceMap: make(map[int]source.Pos),
|
||||||
@@ -45,15 +43,16 @@ func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []object
|
|||||||
// symbol table
|
// symbol table
|
||||||
if symbolTable == nil {
|
if symbolTable == nil {
|
||||||
symbolTable = NewSymbolTable()
|
symbolTable = NewSymbolTable()
|
||||||
|
}
|
||||||
|
|
||||||
for idx, fn := range objects.Builtins {
|
// add builtin functions to the symbol table
|
||||||
symbolTable.DefineBuiltin(idx, fn.Name)
|
for idx, fn := range objects.Builtins {
|
||||||
}
|
symbolTable.DefineBuiltin(idx, fn.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtin modules
|
// builtin modules
|
||||||
if builtinModules == nil {
|
if modules == nil {
|
||||||
builtinModules = make(map[string]bool)
|
modules = objects.NewModuleMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Compiler{
|
return &Compiler{
|
||||||
@@ -64,7 +63,7 @@ func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []object
|
|||||||
scopeIndex: 0,
|
scopeIndex: 0,
|
||||||
loopIndex: -1,
|
loopIndex: -1,
|
||||||
trace: trace,
|
trace: trace,
|
||||||
builtinModules: builtinModules,
|
modules: modules,
|
||||||
compiledModules: make(map[string]*objects.CompiledFunction),
|
compiledModules: make(map[string]*objects.CompiledFunction),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +119,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emit(node, OpGreaterThan)
|
c.emit(node, OpBinaryOp, int(token.Greater))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
} else if node.Token == token.LessEq {
|
} else if node.Token == token.LessEq {
|
||||||
@@ -131,7 +130,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emit(node, OpGreaterThanEqual)
|
c.emit(node, OpBinaryOp, int(token.GreaterEq))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -145,35 +144,35 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
|
|
||||||
switch node.Token {
|
switch node.Token {
|
||||||
case token.Add:
|
case token.Add:
|
||||||
c.emit(node, OpAdd)
|
c.emit(node, OpBinaryOp, int(token.Add))
|
||||||
case token.Sub:
|
case token.Sub:
|
||||||
c.emit(node, OpSub)
|
c.emit(node, OpBinaryOp, int(token.Sub))
|
||||||
case token.Mul:
|
case token.Mul:
|
||||||
c.emit(node, OpMul)
|
c.emit(node, OpBinaryOp, int(token.Mul))
|
||||||
case token.Quo:
|
case token.Quo:
|
||||||
c.emit(node, OpDiv)
|
c.emit(node, OpBinaryOp, int(token.Quo))
|
||||||
case token.Rem:
|
case token.Rem:
|
||||||
c.emit(node, OpRem)
|
c.emit(node, OpBinaryOp, int(token.Rem))
|
||||||
case token.Greater:
|
case token.Greater:
|
||||||
c.emit(node, OpGreaterThan)
|
c.emit(node, OpBinaryOp, int(token.Greater))
|
||||||
case token.GreaterEq:
|
case token.GreaterEq:
|
||||||
c.emit(node, OpGreaterThanEqual)
|
c.emit(node, OpBinaryOp, int(token.GreaterEq))
|
||||||
case token.Equal:
|
case token.Equal:
|
||||||
c.emit(node, OpEqual)
|
c.emit(node, OpEqual)
|
||||||
case token.NotEqual:
|
case token.NotEqual:
|
||||||
c.emit(node, OpNotEqual)
|
c.emit(node, OpNotEqual)
|
||||||
case token.And:
|
case token.And:
|
||||||
c.emit(node, OpBAnd)
|
c.emit(node, OpBinaryOp, int(token.And))
|
||||||
case token.Or:
|
case token.Or:
|
||||||
c.emit(node, OpBOr)
|
c.emit(node, OpBinaryOp, int(token.Or))
|
||||||
case token.Xor:
|
case token.Xor:
|
||||||
c.emit(node, OpBXor)
|
c.emit(node, OpBinaryOp, int(token.Xor))
|
||||||
case token.AndNot:
|
case token.AndNot:
|
||||||
c.emit(node, OpBAndNot)
|
c.emit(node, OpBinaryOp, int(token.AndNot))
|
||||||
case token.Shl:
|
case token.Shl:
|
||||||
c.emit(node, OpBShiftLeft)
|
c.emit(node, OpBinaryOp, int(token.Shl))
|
||||||
case token.Shr:
|
case token.Shr:
|
||||||
c.emit(node, OpBShiftRight)
|
c.emit(node, OpBinaryOp, int(token.Shr))
|
||||||
default:
|
default:
|
||||||
return c.errorf(node, "invalid binary operator: %s", node.Token.String())
|
return c.errorf(node, "invalid binary operator: %s", node.Token.String())
|
||||||
}
|
}
|
||||||
@@ -293,6 +292,15 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case *ast.BlockStmt:
|
case *ast.BlockStmt:
|
||||||
|
if len(node.Stmts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.symbolTable = c.symbolTable.Fork(true)
|
||||||
|
defer func() {
|
||||||
|
c.symbolTable = c.symbolTable.Parent(false)
|
||||||
|
}()
|
||||||
|
|
||||||
for _, stmt := range node.Stmts {
|
for _, stmt := range node.Stmts {
|
||||||
if err := c.Compile(stmt); err != nil {
|
if err := c.Compile(stmt); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -405,10 +413,8 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// add OpReturn if function returns nothing
|
// code optimization
|
||||||
if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) {
|
c.optimizeFunc(node)
|
||||||
c.emit(node, OpReturn)
|
|
||||||
}
|
|
||||||
|
|
||||||
freeSymbols := c.symbolTable.FreeSymbols()
|
freeSymbols := c.symbolTable.FreeSymbols()
|
||||||
numLocals := c.symbolTable.MaxSymbols()
|
numLocals := c.symbolTable.MaxSymbols()
|
||||||
@@ -461,9 +467,9 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
s.LocalAssigned = true
|
s.LocalAssigned = true
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emit(node, OpGetLocal, s.Index)
|
c.emit(node, OpGetLocalPtr, s.Index)
|
||||||
case ScopeFree:
|
case ScopeFree:
|
||||||
c.emit(node, OpGetFree, s.Index)
|
c.emit(node, OpGetFreePtr, s.Index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,13 +493,13 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.Result == nil {
|
if node.Result == nil {
|
||||||
c.emit(node, OpReturn)
|
c.emit(node, OpReturn, 0)
|
||||||
} else {
|
} else {
|
||||||
if err := c.Compile(node.Result); err != nil {
|
if err := c.Compile(node.Result); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emit(node, OpReturnValue)
|
c.emit(node, OpReturn, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
@@ -510,21 +516,57 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
c.emit(node, OpCall, len(node.Args))
|
c.emit(node, OpCall, len(node.Args))
|
||||||
|
|
||||||
case *ast.ImportExpr:
|
case *ast.ImportExpr:
|
||||||
if c.builtinModules[node.ModuleName] {
|
if node.ModuleName == "" {
|
||||||
if len(node.ModuleName) > tengo.MaxStringLen {
|
return c.errorf(node, "empty module name")
|
||||||
return c.error(node, objects.ErrStringLimit)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.ModuleName}))
|
if mod := c.modules.Get(node.ModuleName); mod != nil {
|
||||||
c.emit(node, OpGetBuiltinModule)
|
v, err := mod.Import(node.ModuleName)
|
||||||
} else {
|
|
||||||
userMod, err := c.compileModule(node)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.emit(node, OpConstant, c.addConstant(userMod))
|
switch v := v.(type) {
|
||||||
|
case []byte: // module written in Tengo
|
||||||
|
compiled, err := c.compileModule(node, node.ModuleName, node.ModuleName, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.emit(node, OpConstant, c.addConstant(compiled))
|
||||||
|
c.emit(node, OpCall, 0)
|
||||||
|
case objects.Object: // builtin module
|
||||||
|
c.emit(node, OpConstant, c.addConstant(v))
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("invalid import value type: %T", v))
|
||||||
|
}
|
||||||
|
} else if c.allowFileImport {
|
||||||
|
moduleName := node.ModuleName
|
||||||
|
if !strings.HasSuffix(moduleName, ".tengo") {
|
||||||
|
moduleName += ".tengo"
|
||||||
|
}
|
||||||
|
|
||||||
|
modulePath, err := filepath.Abs(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)
|
||||||
|
if err != nil {
|
||||||
|
return c.errorf(node, "module file read error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
compiled, err := c.compileModule(node, moduleName, modulePath, moduleSrc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.emit(node, OpConstant, c.addConstant(compiled))
|
||||||
c.emit(node, OpCall, 0)
|
c.emit(node, OpCall, 0)
|
||||||
|
} else {
|
||||||
|
return c.errorf(node, "module '%s' not found", node.ModuleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.ExportStmt:
|
case *ast.ExportStmt:
|
||||||
@@ -543,7 +585,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.emit(node, OpImmutable)
|
c.emit(node, OpImmutable)
|
||||||
c.emit(node, OpReturnValue)
|
c.emit(node, OpReturn, 1)
|
||||||
|
|
||||||
case *ast.ErrorExpr:
|
case *ast.ErrorExpr:
|
||||||
if err := c.Compile(node.Expr); err != nil {
|
if err := c.Compile(node.Expr); err != nil {
|
||||||
@@ -602,18 +644,16 @@ func (c *Compiler) Bytecode() *Bytecode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetModuleLoader sets or replaces the current module loader.
|
// EnableFileImport enables or disables module loading from local files.
|
||||||
// Note that the module loader is used for user modules,
|
// Local file modules are disabled by default.
|
||||||
// not for the standard modules.
|
func (c *Compiler) EnableFileImport(enable bool) {
|
||||||
func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) {
|
c.allowFileImport = enable
|
||||||
c.moduleLoader = moduleLoader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) fork(file *source.File, moduleName string, symbolTable *SymbolTable) *Compiler {
|
func (c *Compiler) fork(file *source.File, modulePath string, symbolTable *SymbolTable) *Compiler {
|
||||||
child := NewCompiler(file, symbolTable, nil, c.builtinModules, c.trace)
|
child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
|
||||||
child.moduleName = moduleName // name of the module to compile
|
child.modulePath = modulePath // module file path
|
||||||
child.parent = c // parent to set to current compiler
|
child.parent = c // parent to set to current compiler
|
||||||
child.moduleLoader = c.moduleLoader // share module loader
|
|
||||||
|
|
||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
@@ -657,33 +697,6 @@ func (c *Compiler) addInstruction(b []byte) int {
|
|||||||
return posNewIns
|
return posNewIns
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) setLastInstruction(op Opcode, pos int) {
|
|
||||||
c.scopes[c.scopeIndex].lastInstructions[1] = c.scopes[c.scopeIndex].lastInstructions[0]
|
|
||||||
|
|
||||||
c.scopes[c.scopeIndex].lastInstructions[0].Opcode = op
|
|
||||||
c.scopes[c.scopeIndex].lastInstructions[0].Position = pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) lastInstructionIs(op Opcode) bool {
|
|
||||||
if len(c.currentInstructions()) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.scopes[c.scopeIndex].lastInstructions[0].Opcode == op
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) removeLastInstruction() {
|
|
||||||
lastPos := c.scopes[c.scopeIndex].lastInstructions[0].Position
|
|
||||||
|
|
||||||
if c.trace != nil {
|
|
||||||
c.printTrace(fmt.Sprintf("DELET %s",
|
|
||||||
FormatInstructions(c.scopes[c.scopeIndex].instructions[lastPos:], lastPos)[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
c.scopes[c.scopeIndex].instructions = c.currentInstructions()[:lastPos]
|
|
||||||
c.scopes[c.scopeIndex].lastInstructions[0] = c.scopes[c.scopeIndex].lastInstructions[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) replaceInstruction(pos int, inst []byte) {
|
func (c *Compiler) replaceInstruction(pos int, inst []byte) {
|
||||||
copy(c.currentInstructions()[pos:], inst)
|
copy(c.currentInstructions()[pos:], inst)
|
||||||
|
|
||||||
@@ -700,6 +713,92 @@ func (c *Compiler) changeOperand(opPos int, operand ...int) {
|
|||||||
c.replaceInstruction(opPos, inst)
|
c.replaceInstruction(opPos, inst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimizeFunc performs some code-level optimization for the current function instructions
|
||||||
|
// it removes unreachable (dead code) instructions and adds "returns" instruction if needed.
|
||||||
|
func (c *Compiler) optimizeFunc(node ast.Node) {
|
||||||
|
// any instructions between RETURN and the function end
|
||||||
|
// or instructions between RETURN and jump target position
|
||||||
|
// are considered as unreachable.
|
||||||
|
|
||||||
|
// pass 1. identify all jump destinations
|
||||||
|
dsts := make(map[int]bool)
|
||||||
|
iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool {
|
||||||
|
switch opcode {
|
||||||
|
case OpJump, OpJumpFalsy, OpAndJump, OpOrJump:
|
||||||
|
dsts[operands[0]] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
var newInsts []byte
|
||||||
|
|
||||||
|
// pass 2. eliminate dead code
|
||||||
|
posMap := make(map[int]int) // old position to new position
|
||||||
|
var dstIdx int
|
||||||
|
var deadCode bool
|
||||||
|
iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool {
|
||||||
|
switch {
|
||||||
|
case opcode == OpReturn:
|
||||||
|
if deadCode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
deadCode = true
|
||||||
|
case dsts[pos]:
|
||||||
|
dstIdx++
|
||||||
|
deadCode = false
|
||||||
|
case deadCode:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
posMap[pos] = len(newInsts)
|
||||||
|
newInsts = append(newInsts, MakeInstruction(opcode, operands...)...)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// pass 3. update jump positions
|
||||||
|
var lastOp Opcode
|
||||||
|
var appendReturn bool
|
||||||
|
endPos := len(c.scopes[c.scopeIndex].instructions)
|
||||||
|
iterateInstructions(newInsts, func(pos int, opcode Opcode, operands []int) bool {
|
||||||
|
switch opcode {
|
||||||
|
case OpJump, OpJumpFalsy, OpAndJump, OpOrJump:
|
||||||
|
newDst, ok := posMap[operands[0]]
|
||||||
|
if ok {
|
||||||
|
copy(newInsts[pos:], MakeInstruction(opcode, newDst))
|
||||||
|
} else if endPos == operands[0] {
|
||||||
|
// there's a jump instruction that jumps to the end of function
|
||||||
|
// compiler should append "return".
|
||||||
|
appendReturn = true
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("invalid jump position: %d", newDst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastOp = opcode
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if lastOp != OpReturn {
|
||||||
|
appendReturn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass 4. update source map
|
||||||
|
newSourceMap := make(map[int]source.Pos)
|
||||||
|
for pos, srcPos := range c.scopes[c.scopeIndex].sourceMap {
|
||||||
|
newPos, ok := posMap[pos]
|
||||||
|
if ok {
|
||||||
|
newSourceMap[newPos] = srcPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.scopes[c.scopeIndex].instructions = newInsts
|
||||||
|
c.scopes[c.scopeIndex].sourceMap = newSourceMap
|
||||||
|
|
||||||
|
// append "return"
|
||||||
|
if appendReturn {
|
||||||
|
c.emit(node, OpReturn, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
|
func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
|
||||||
filePos := source.NoPos
|
filePos := source.NoPos
|
||||||
if node != nil {
|
if node != nil {
|
||||||
@@ -709,7 +808,6 @@ func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
|
|||||||
inst := MakeInstruction(opcode, operands...)
|
inst := MakeInstruction(opcode, operands...)
|
||||||
pos := c.addInstruction(inst)
|
pos := c.addInstruction(inst)
|
||||||
c.scopes[c.scopeIndex].sourceMap[pos] = filePos
|
c.scopes[c.scopeIndex].sourceMap[pos] = filePos
|
||||||
c.setLastInstruction(opcode, pos)
|
|
||||||
|
|
||||||
if c.trace != nil {
|
if c.trace != nil {
|
||||||
c.printTrace(fmt.Sprintf("EMIT %s",
|
c.printTrace(fmt.Sprintf("EMIT %s",
|
||||||
|
|||||||
22
vendor/github.com/d5/tengo/compiler/compiler_assign.go
generated
vendored
22
vendor/github.com/d5/tengo/compiler/compiler_assign.go
generated
vendored
@@ -51,27 +51,27 @@ func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.To
|
|||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case token.AddAssign:
|
case token.AddAssign:
|
||||||
c.emit(node, OpAdd)
|
c.emit(node, OpBinaryOp, int(token.Add))
|
||||||
case token.SubAssign:
|
case token.SubAssign:
|
||||||
c.emit(node, OpSub)
|
c.emit(node, OpBinaryOp, int(token.Sub))
|
||||||
case token.MulAssign:
|
case token.MulAssign:
|
||||||
c.emit(node, OpMul)
|
c.emit(node, OpBinaryOp, int(token.Mul))
|
||||||
case token.QuoAssign:
|
case token.QuoAssign:
|
||||||
c.emit(node, OpDiv)
|
c.emit(node, OpBinaryOp, int(token.Quo))
|
||||||
case token.RemAssign:
|
case token.RemAssign:
|
||||||
c.emit(node, OpRem)
|
c.emit(node, OpBinaryOp, int(token.Rem))
|
||||||
case token.AndAssign:
|
case token.AndAssign:
|
||||||
c.emit(node, OpBAnd)
|
c.emit(node, OpBinaryOp, int(token.And))
|
||||||
case token.OrAssign:
|
case token.OrAssign:
|
||||||
c.emit(node, OpBOr)
|
c.emit(node, OpBinaryOp, int(token.Or))
|
||||||
case token.AndNotAssign:
|
case token.AndNotAssign:
|
||||||
c.emit(node, OpBAndNot)
|
c.emit(node, OpBinaryOp, int(token.AndNot))
|
||||||
case token.XorAssign:
|
case token.XorAssign:
|
||||||
c.emit(node, OpBXor)
|
c.emit(node, OpBinaryOp, int(token.Xor))
|
||||||
case token.ShlAssign:
|
case token.ShlAssign:
|
||||||
c.emit(node, OpBShiftLeft)
|
c.emit(node, OpBinaryOp, int(token.Shl))
|
||||||
case token.ShrAssign:
|
case token.ShrAssign:
|
||||||
c.emit(node, OpBShiftRight)
|
c.emit(node, OpBinaryOp, int(token.Shr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// compile selector expressions (right to left)
|
// compile selector expressions (right to left)
|
||||||
|
|||||||
91
vendor/github.com/d5/tengo/compiler/compiler_module.go
generated
vendored
91
vendor/github.com/d5/tengo/compiler/compiler_module.go
generated
vendored
@@ -1,72 +1,31 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/d5/tengo/compiler/ast"
|
"github.com/d5/tengo/compiler/ast"
|
||||||
"github.com/d5/tengo/compiler/parser"
|
"github.com/d5/tengo/compiler/parser"
|
||||||
"github.com/d5/tengo/objects"
|
"github.com/d5/tengo/objects"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) compileModule(expr *ast.ImportExpr) (*objects.CompiledFunction, error) {
|
func (c *Compiler) checkCyclicImports(node ast.Node, modulePath string) error {
|
||||||
compiledModule, exists := c.loadCompiledModule(expr.ModuleName)
|
if c.modulePath == modulePath {
|
||||||
if exists {
|
return c.errorf(node, "cyclic module import: %s", modulePath)
|
||||||
return compiledModule, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleName := expr.ModuleName
|
|
||||||
|
|
||||||
// read module source from loader
|
|
||||||
var moduleSrc []byte
|
|
||||||
if c.moduleLoader == nil {
|
|
||||||
// default loader: read from local file
|
|
||||||
if !strings.HasSuffix(moduleName, ".tengo") {
|
|
||||||
moduleName += ".tengo"
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.checkCyclicImports(expr, moduleName); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
moduleSrc, err = ioutil.ReadFile(moduleName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, c.errorf(expr, "module file read error: %s", err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := c.checkCyclicImports(expr, moduleName); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
moduleSrc, err = c.moduleLoader(moduleName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compiledModule, err := c.doCompileModule(moduleName, moduleSrc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.storeCompiledModule(moduleName, compiledModule)
|
|
||||||
|
|
||||||
return compiledModule, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) checkCyclicImports(node ast.Node, moduleName string) error {
|
|
||||||
if c.moduleName == moduleName {
|
|
||||||
return c.errorf(node, "cyclic module import: %s", moduleName)
|
|
||||||
} else if c.parent != nil {
|
} else if c.parent != nil {
|
||||||
return c.parent.checkCyclicImports(node, moduleName)
|
return c.parent.checkCyclicImports(node, modulePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.CompiledFunction, error) {
|
func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, src []byte) (*objects.CompiledFunction, error) {
|
||||||
|
if err := c.checkCyclicImports(node, modulePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
compiledModule, exists := c.loadCompiledModule(modulePath)
|
||||||
|
if exists {
|
||||||
|
return compiledModule, nil
|
||||||
|
}
|
||||||
|
|
||||||
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
|
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
|
||||||
p := parser.NewParser(modFile, src, nil)
|
p := parser.NewParser(modFile, src, nil)
|
||||||
file, err := p.ParseFile()
|
file, err := p.ParseFile()
|
||||||
@@ -85,36 +44,36 @@ func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.Comp
|
|||||||
symbolTable = symbolTable.Fork(false)
|
symbolTable = symbolTable.Fork(false)
|
||||||
|
|
||||||
// compile module
|
// compile module
|
||||||
moduleCompiler := c.fork(modFile, moduleName, symbolTable)
|
moduleCompiler := c.fork(modFile, modulePath, symbolTable)
|
||||||
if err := moduleCompiler.Compile(file); err != nil {
|
if err := moduleCompiler.Compile(file); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// add OpReturn (== export undefined) if export is missing
|
// code optimization
|
||||||
if !moduleCompiler.lastInstructionIs(OpReturnValue) {
|
moduleCompiler.optimizeFunc(node)
|
||||||
moduleCompiler.emit(nil, OpReturn)
|
|
||||||
}
|
|
||||||
|
|
||||||
compiledFunc := moduleCompiler.Bytecode().MainFunction
|
compiledFunc := moduleCompiler.Bytecode().MainFunction
|
||||||
compiledFunc.NumLocals = symbolTable.MaxSymbols()
|
compiledFunc.NumLocals = symbolTable.MaxSymbols()
|
||||||
|
|
||||||
|
c.storeCompiledModule(modulePath, compiledFunc)
|
||||||
|
|
||||||
return compiledFunc, nil
|
return compiledFunc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) loadCompiledModule(moduleName string) (mod *objects.CompiledFunction, ok bool) {
|
func (c *Compiler) loadCompiledModule(modulePath string) (mod *objects.CompiledFunction, ok bool) {
|
||||||
if c.parent != nil {
|
if c.parent != nil {
|
||||||
return c.parent.loadCompiledModule(moduleName)
|
return c.parent.loadCompiledModule(modulePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
mod, ok = c.compiledModules[moduleName]
|
mod, ok = c.compiledModules[modulePath]
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) storeCompiledModule(moduleName string, module *objects.CompiledFunction) {
|
func (c *Compiler) storeCompiledModule(modulePath string, module *objects.CompiledFunction) {
|
||||||
if c.parent != nil {
|
if c.parent != nil {
|
||||||
c.parent.storeCompiledModule(moduleName, module)
|
c.parent.storeCompiledModule(modulePath, module)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.compiledModules[moduleName] = module
|
c.compiledModules[modulePath] = module
|
||||||
}
|
}
|
||||||
|
|||||||
13
vendor/github.com/d5/tengo/compiler/instructions.go
generated
vendored
13
vendor/github.com/d5/tengo/compiler/instructions.go
generated
vendored
@@ -57,3 +57,16 @@ func FormatInstructions(b []byte, posOffset int) []string {
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func iterateInstructions(b []byte, fn func(pos int, opcode Opcode, operands []int) bool) {
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
numOperands := OpcodeOperands[Opcode(b[i])]
|
||||||
|
operands, read := ReadOperands(numOperands, b[i+1:])
|
||||||
|
|
||||||
|
if !fn(i, b[i], operands) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
i += read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
282
vendor/github.com/d5/tengo/compiler/opcodes.go
generated
vendored
282
vendor/github.com/d5/tengo/compiler/opcodes.go
generated
vendored
@@ -5,173 +5,137 @@ type Opcode = byte
|
|||||||
|
|
||||||
// List of opcodes
|
// List of opcodes
|
||||||
const (
|
const (
|
||||||
OpConstant Opcode = iota // Load constant
|
OpConstant Opcode = iota // Load constant
|
||||||
OpAdd // Add
|
OpBComplement // bitwise complement
|
||||||
OpSub // Sub
|
OpPop // Pop
|
||||||
OpMul // Multiply
|
OpTrue // Push true
|
||||||
OpDiv // Divide
|
OpFalse // Push false
|
||||||
OpRem // Remainder
|
OpEqual // Equal ==
|
||||||
OpBAnd // bitwise AND
|
OpNotEqual // Not equal !=
|
||||||
OpBOr // bitwise OR
|
OpMinus // Minus -
|
||||||
OpBXor // bitwise XOR
|
OpLNot // Logical not !
|
||||||
OpBShiftLeft // bitwise shift left
|
OpJumpFalsy // Jump if falsy
|
||||||
OpBShiftRight // bitwise shift right
|
OpAndJump // Logical AND jump
|
||||||
OpBAndNot // bitwise AND NOT
|
OpOrJump // Logical OR jump
|
||||||
OpBComplement // bitwise complement
|
OpJump // Jump
|
||||||
OpPop // Pop
|
OpNull // Push null
|
||||||
OpTrue // Push true
|
OpArray // Array object
|
||||||
OpFalse // Push false
|
OpMap // Map object
|
||||||
OpEqual // Equal ==
|
OpError // Error object
|
||||||
OpNotEqual // Not equal !=
|
OpImmutable // Immutable object
|
||||||
OpGreaterThan // Greater than >=
|
OpIndex // Index operation
|
||||||
OpGreaterThanEqual // Greater than or equal to >=
|
OpSliceIndex // Slice operation
|
||||||
OpMinus // Minus -
|
OpCall // Call function
|
||||||
OpLNot // Logical not !
|
OpReturn // Return
|
||||||
OpJumpFalsy // Jump if falsy
|
OpGetGlobal // Get global variable
|
||||||
OpAndJump // Logical AND jump
|
OpSetGlobal // Set global variable
|
||||||
OpOrJump // Logical OR jump
|
OpSetSelGlobal // Set global variable using selectors
|
||||||
OpJump // Jump
|
OpGetLocal // Get local variable
|
||||||
OpNull // Push null
|
OpSetLocal // Set local variable
|
||||||
OpArray // Array object
|
OpDefineLocal // Define local variable
|
||||||
OpMap // Map object
|
OpSetSelLocal // Set local variable using selectors
|
||||||
OpError // Error object
|
OpGetFreePtr // Get free variable pointer object
|
||||||
OpImmutable // Immutable object
|
OpGetFree // Get free variables
|
||||||
OpIndex // Index operation
|
OpSetFree // Set free variables
|
||||||
OpSliceIndex // Slice operation
|
OpGetLocalPtr // Get local variable as a pointer
|
||||||
OpCall // Call function
|
OpSetSelFree // Set free variables using selectors
|
||||||
OpReturn // Return
|
OpGetBuiltin // Get builtin function
|
||||||
OpReturnValue // Return value
|
OpClosure // Push closure
|
||||||
OpGetGlobal // Get global variable
|
OpIteratorInit // Iterator init
|
||||||
OpSetGlobal // Set global variable
|
OpIteratorNext // Iterator next
|
||||||
OpSetSelGlobal // Set global variable using selectors
|
OpIteratorKey // Iterator key
|
||||||
OpGetLocal // Get local variable
|
OpIteratorValue // Iterator value
|
||||||
OpSetLocal // Set local variable
|
OpBinaryOp // Binary Operation
|
||||||
OpDefineLocal // Define local variable
|
|
||||||
OpSetSelLocal // Set local variable using selectors
|
|
||||||
OpGetFree // Get free variables
|
|
||||||
OpSetFree // Set free variables
|
|
||||||
OpSetSelFree // Set free variables using selectors
|
|
||||||
OpGetBuiltin // Get builtin function
|
|
||||||
OpGetBuiltinModule // Get builtin module
|
|
||||||
OpClosure // Push closure
|
|
||||||
OpIteratorInit // Iterator init
|
|
||||||
OpIteratorNext // Iterator next
|
|
||||||
OpIteratorKey // Iterator key
|
|
||||||
OpIteratorValue // Iterator value
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OpcodeNames is opcode names.
|
// OpcodeNames is opcode names.
|
||||||
var OpcodeNames = [...]string{
|
var OpcodeNames = [...]string{
|
||||||
OpConstant: "CONST",
|
OpConstant: "CONST",
|
||||||
OpPop: "POP",
|
OpPop: "POP",
|
||||||
OpTrue: "TRUE",
|
OpTrue: "TRUE",
|
||||||
OpFalse: "FALSE",
|
OpFalse: "FALSE",
|
||||||
OpAdd: "ADD",
|
OpBComplement: "NEG",
|
||||||
OpSub: "SUB",
|
OpEqual: "EQL",
|
||||||
OpMul: "MUL",
|
OpNotEqual: "NEQ",
|
||||||
OpDiv: "DIV",
|
OpMinus: "NEG",
|
||||||
OpRem: "REM",
|
OpLNot: "NOT",
|
||||||
OpBAnd: "AND",
|
OpJumpFalsy: "JMPF",
|
||||||
OpBOr: "OR",
|
OpAndJump: "ANDJMP",
|
||||||
OpBXor: "XOR",
|
OpOrJump: "ORJMP",
|
||||||
OpBAndNot: "ANDN",
|
OpJump: "JMP",
|
||||||
OpBShiftLeft: "SHL",
|
OpNull: "NULL",
|
||||||
OpBShiftRight: "SHR",
|
OpGetGlobal: "GETG",
|
||||||
OpBComplement: "NEG",
|
OpSetGlobal: "SETG",
|
||||||
OpEqual: "EQL",
|
OpSetSelGlobal: "SETSG",
|
||||||
OpNotEqual: "NEQ",
|
OpArray: "ARR",
|
||||||
OpGreaterThan: "GTR",
|
OpMap: "MAP",
|
||||||
OpGreaterThanEqual: "GEQ",
|
OpError: "ERROR",
|
||||||
OpMinus: "NEG",
|
OpImmutable: "IMMUT",
|
||||||
OpLNot: "NOT",
|
OpIndex: "INDEX",
|
||||||
OpJumpFalsy: "JMPF",
|
OpSliceIndex: "SLICE",
|
||||||
OpAndJump: "ANDJMP",
|
OpCall: "CALL",
|
||||||
OpOrJump: "ORJMP",
|
OpReturn: "RET",
|
||||||
OpJump: "JMP",
|
OpGetLocal: "GETL",
|
||||||
OpNull: "NULL",
|
OpSetLocal: "SETL",
|
||||||
OpGetGlobal: "GETG",
|
OpDefineLocal: "DEFL",
|
||||||
OpSetGlobal: "SETG",
|
OpSetSelLocal: "SETSL",
|
||||||
OpSetSelGlobal: "SETSG",
|
OpGetBuiltin: "BUILTIN",
|
||||||
OpArray: "ARR",
|
OpClosure: "CLOSURE",
|
||||||
OpMap: "MAP",
|
OpGetFreePtr: "GETFP",
|
||||||
OpError: "ERROR",
|
OpGetFree: "GETF",
|
||||||
OpImmutable: "IMMUT",
|
OpSetFree: "SETF",
|
||||||
OpIndex: "INDEX",
|
OpGetLocalPtr: "GETLP",
|
||||||
OpSliceIndex: "SLICE",
|
OpSetSelFree: "SETSF",
|
||||||
OpCall: "CALL",
|
OpIteratorInit: "ITER",
|
||||||
OpReturn: "RET",
|
OpIteratorNext: "ITNXT",
|
||||||
OpReturnValue: "RETVAL",
|
OpIteratorKey: "ITKEY",
|
||||||
OpGetLocal: "GETL",
|
OpIteratorValue: "ITVAL",
|
||||||
OpSetLocal: "SETL",
|
OpBinaryOp: "BINARYOP",
|
||||||
OpDefineLocal: "DEFL",
|
|
||||||
OpSetSelLocal: "SETSL",
|
|
||||||
OpGetBuiltin: "BUILTIN",
|
|
||||||
OpGetBuiltinModule: "BLTMOD",
|
|
||||||
OpClosure: "CLOSURE",
|
|
||||||
OpGetFree: "GETF",
|
|
||||||
OpSetFree: "SETF",
|
|
||||||
OpSetSelFree: "SETSF",
|
|
||||||
OpIteratorInit: "ITER",
|
|
||||||
OpIteratorNext: "ITNXT",
|
|
||||||
OpIteratorKey: "ITKEY",
|
|
||||||
OpIteratorValue: "ITVAL",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpcodeOperands is the number of operands.
|
// OpcodeOperands is the number of operands.
|
||||||
var OpcodeOperands = [...][]int{
|
var OpcodeOperands = [...][]int{
|
||||||
OpConstant: {2},
|
OpConstant: {2},
|
||||||
OpPop: {},
|
OpPop: {},
|
||||||
OpTrue: {},
|
OpTrue: {},
|
||||||
OpFalse: {},
|
OpFalse: {},
|
||||||
OpAdd: {},
|
OpBComplement: {},
|
||||||
OpSub: {},
|
OpEqual: {},
|
||||||
OpMul: {},
|
OpNotEqual: {},
|
||||||
OpDiv: {},
|
OpMinus: {},
|
||||||
OpRem: {},
|
OpLNot: {},
|
||||||
OpBAnd: {},
|
OpJumpFalsy: {2},
|
||||||
OpBOr: {},
|
OpAndJump: {2},
|
||||||
OpBXor: {},
|
OpOrJump: {2},
|
||||||
OpBAndNot: {},
|
OpJump: {2},
|
||||||
OpBShiftLeft: {},
|
OpNull: {},
|
||||||
OpBShiftRight: {},
|
OpGetGlobal: {2},
|
||||||
OpBComplement: {},
|
OpSetGlobal: {2},
|
||||||
OpEqual: {},
|
OpSetSelGlobal: {2, 1},
|
||||||
OpNotEqual: {},
|
OpArray: {2},
|
||||||
OpGreaterThan: {},
|
OpMap: {2},
|
||||||
OpGreaterThanEqual: {},
|
OpError: {},
|
||||||
OpMinus: {},
|
OpImmutable: {},
|
||||||
OpLNot: {},
|
OpIndex: {},
|
||||||
OpJumpFalsy: {2},
|
OpSliceIndex: {},
|
||||||
OpAndJump: {2},
|
OpCall: {1},
|
||||||
OpOrJump: {2},
|
OpReturn: {1},
|
||||||
OpJump: {2},
|
OpGetLocal: {1},
|
||||||
OpNull: {},
|
OpSetLocal: {1},
|
||||||
OpGetGlobal: {2},
|
OpDefineLocal: {1},
|
||||||
OpSetGlobal: {2},
|
OpSetSelLocal: {1, 1},
|
||||||
OpSetSelGlobal: {2, 1},
|
OpGetBuiltin: {1},
|
||||||
OpArray: {2},
|
OpClosure: {2, 1},
|
||||||
OpMap: {2},
|
OpGetFreePtr: {1},
|
||||||
OpError: {},
|
OpGetFree: {1},
|
||||||
OpImmutable: {},
|
OpSetFree: {1},
|
||||||
OpIndex: {},
|
OpGetLocalPtr: {1},
|
||||||
OpSliceIndex: {},
|
OpSetSelFree: {1, 1},
|
||||||
OpCall: {1},
|
OpIteratorInit: {},
|
||||||
OpReturn: {},
|
OpIteratorNext: {},
|
||||||
OpReturnValue: {},
|
OpIteratorKey: {},
|
||||||
OpGetLocal: {1},
|
OpIteratorValue: {},
|
||||||
OpSetLocal: {1},
|
OpBinaryOp: {1},
|
||||||
OpDefineLocal: {1},
|
|
||||||
OpSetSelLocal: {1, 1},
|
|
||||||
OpGetBuiltin: {1},
|
|
||||||
OpGetBuiltinModule: {},
|
|
||||||
OpClosure: {2, 1},
|
|
||||||
OpGetFree: {1},
|
|
||||||
OpSetFree: {1},
|
|
||||||
OpSetSelFree: {1, 1},
|
|
||||||
OpIteratorInit: {},
|
|
||||||
OpIteratorNext: {},
|
|
||||||
OpIteratorKey: {},
|
|
||||||
OpIteratorValue: {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOperands reads operands from the bytecode.
|
// ReadOperands reads operands from the bytecode.
|
||||||
|
|||||||
4
vendor/github.com/d5/tengo/compiler/symbol_table.go
generated
vendored
4
vendor/github.com/d5/tengo/compiler/symbol_table.go
generated
vendored
@@ -64,9 +64,7 @@ func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.block {
|
depth++
|
||||||
depth++
|
|
||||||
}
|
|
||||||
|
|
||||||
// if symbol is defined in parent table and if it's not global/builtin
|
// if symbol is defined in parent table and if it's not global/builtin
|
||||||
// then it's free variable.
|
// then it's free variable.
|
||||||
|
|||||||
37
vendor/github.com/d5/tengo/objects/break.go
generated
vendored
37
vendor/github.com/d5/tengo/objects/break.go
generated
vendored
@@ -1,37 +0,0 @@
|
|||||||
package objects
|
|
||||||
|
|
||||||
import "github.com/d5/tengo/compiler/token"
|
|
||||||
|
|
||||||
// Break represents a break statement.
|
|
||||||
type Break struct{}
|
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
|
||||||
func (o *Break) TypeName() string {
|
|
||||||
return "break"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Break) String() string {
|
|
||||||
return "<break>"
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinaryOp returns another object that is the result of
|
|
||||||
// a given binary operator and a right-hand side object.
|
|
||||||
func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|
||||||
return nil, ErrInvalidOperator
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a copy of the type.
|
|
||||||
func (o *Break) Copy() Object {
|
|
||||||
return &Break{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFalsy returns true if the value of the type is falsy.
|
|
||||||
func (o *Break) IsFalsy() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns true if the value of the type
|
|
||||||
// is equal to the value of another object.
|
|
||||||
func (o *Break) Equals(x Object) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
60
vendor/github.com/d5/tengo/objects/builtin_json.go
generated
vendored
60
vendor/github.com/d5/tengo/objects/builtin_json.go
generated
vendored
@@ -1,60 +0,0 @@
|
|||||||
package objects
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/d5/tengo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// to_json(v object) => bytes
|
|
||||||
func builtinToJSON(args ...Object) (Object, error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := json.Marshal(objectToInterface(args[0]))
|
|
||||||
if err != nil {
|
|
||||||
return &Error{Value: &String{Value: err.Error()}}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(res) > tengo.MaxBytesLen {
|
|
||||||
return nil, ErrBytesLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Bytes{Value: res}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// from_json(data string/bytes) => object
|
|
||||||
func builtinFromJSON(args ...Object) (Object, error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
var target interface{}
|
|
||||||
|
|
||||||
switch o := args[0].(type) {
|
|
||||||
case *Bytes:
|
|
||||||
err := json.Unmarshal(o.Value, &target)
|
|
||||||
if err != nil {
|
|
||||||
return &Error{Value: &String{Value: err.Error()}}, nil
|
|
||||||
}
|
|
||||||
case *String:
|
|
||||||
err := json.Unmarshal([]byte(o.Value), &target)
|
|
||||||
if err != nil {
|
|
||||||
return &Error{Value: &String{Value: err.Error()}}, nil
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, ErrInvalidArgumentType{
|
|
||||||
Name: "first",
|
|
||||||
Expected: "bytes/string",
|
|
||||||
Found: args[0].TypeName(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := FromInterface(target)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
23
vendor/github.com/d5/tengo/objects/builtin_module.go
generated
vendored
Normal file
23
vendor/github.com/d5/tengo/objects/builtin_module.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// BuiltinModule is an importable module that's written in Go.
|
||||||
|
type BuiltinModule struct {
|
||||||
|
Attrs map[string]Object
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import returns an immutable map for the module.
|
||||||
|
func (m *BuiltinModule) Import(moduleName string) (interface{}, error) {
|
||||||
|
return m.AsImmutableMap(moduleName), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsImmutableMap converts builtin module into an immutable map.
|
||||||
|
func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap {
|
||||||
|
attrs := make(map[string]Object, len(m.Attrs))
|
||||||
|
for k, v := range m.Attrs {
|
||||||
|
attrs[k] = v.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs["__module_name__"] = &String{Value: moduleName}
|
||||||
|
|
||||||
|
return &ImmutableMap{Value: attrs}
|
||||||
|
}
|
||||||
83
vendor/github.com/d5/tengo/objects/builtin_print.go
generated
vendored
83
vendor/github.com/d5/tengo/objects/builtin_print.go
generated
vendored
@@ -1,83 +0,0 @@
|
|||||||
package objects
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/d5/tengo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// print(args...)
|
|
||||||
func builtinPrint(args ...Object) (Object, error) {
|
|
||||||
for _, arg := range args {
|
|
||||||
if str, ok := arg.(*String); ok {
|
|
||||||
fmt.Println(str.Value)
|
|
||||||
} else {
|
|
||||||
fmt.Println(arg.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf("format", args...)
|
|
||||||
func builtinPrintf(args ...Object) (Object, error) {
|
|
||||||
numArgs := len(args)
|
|
||||||
if numArgs == 0 {
|
|
||||||
return nil, ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
format, ok := args[0].(*String)
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrInvalidArgumentType{
|
|
||||||
Name: "format",
|
|
||||||
Expected: "string",
|
|
||||||
Found: args[0].TypeName(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if numArgs == 1 {
|
|
||||||
fmt.Print(format)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
formatArgs := make([]interface{}, numArgs-1, numArgs-1)
|
|
||||||
for idx, arg := range args[1:] {
|
|
||||||
formatArgs[idx] = objectToInterface(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf(format.Value, formatArgs...)
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sprintf("format", args...)
|
|
||||||
func builtinSprintf(args ...Object) (Object, error) {
|
|
||||||
numArgs := len(args)
|
|
||||||
if numArgs == 0 {
|
|
||||||
return nil, ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
format, ok := args[0].(*String)
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrInvalidArgumentType{
|
|
||||||
Name: "format",
|
|
||||||
Expected: "string",
|
|
||||||
Found: args[0].TypeName(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if numArgs == 1 {
|
|
||||||
return format, nil // okay to return 'format' directly as String is immutable
|
|
||||||
}
|
|
||||||
|
|
||||||
formatArgs := make([]interface{}, numArgs-1, numArgs-1)
|
|
||||||
for idx, arg := range args[1:] {
|
|
||||||
formatArgs[idx] = objectToInterface(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := fmt.Sprintf(format.Value, formatArgs...)
|
|
||||||
|
|
||||||
if len(s) > tengo.MaxStringLen {
|
|
||||||
return nil, ErrStringLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
return &String{Value: s}, nil
|
|
||||||
}
|
|
||||||
12
vendor/github.com/d5/tengo/objects/builtin_type_checks.go
generated
vendored
12
vendor/github.com/d5/tengo/objects/builtin_type_checks.go
generated
vendored
@@ -181,3 +181,15 @@ func builtinIsCallable(args ...Object) (Object, error) {
|
|||||||
|
|
||||||
return FalseValue, nil
|
return FalseValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func builtinIsIterable(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(Iterable); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|||||||
60
vendor/github.com/d5/tengo/objects/builtins.go
generated
vendored
60
vendor/github.com/d5/tengo/objects/builtins.go
generated
vendored
@@ -2,19 +2,7 @@ package objects
|
|||||||
|
|
||||||
// Builtins contains all default builtin functions.
|
// Builtins contains all default builtin functions.
|
||||||
// Use GetBuiltinFunctions instead of accessing Builtins directly.
|
// Use GetBuiltinFunctions instead of accessing Builtins directly.
|
||||||
var Builtins = []BuiltinFunction{
|
var Builtins = []*BuiltinFunction{
|
||||||
{
|
|
||||||
Name: "print",
|
|
||||||
Value: builtinPrint,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "printf",
|
|
||||||
Value: builtinPrintf,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "sprintf",
|
|
||||||
Value: builtinSprintf,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "len",
|
Name: "len",
|
||||||
Value: builtinLen,
|
Value: builtinLen,
|
||||||
@@ -95,6 +83,10 @@ var Builtins = []BuiltinFunction{
|
|||||||
Name: "is_immutable_map",
|
Name: "is_immutable_map",
|
||||||
Value: builtinIsImmutableMap,
|
Value: builtinIsImmutableMap,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "is_iterable",
|
||||||
|
Value: builtinIsIterable,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "is_time",
|
Name: "is_time",
|
||||||
Value: builtinIsTime,
|
Value: builtinIsTime,
|
||||||
@@ -115,50 +107,8 @@ var Builtins = []BuiltinFunction{
|
|||||||
Name: "is_callable",
|
Name: "is_callable",
|
||||||
Value: builtinIsCallable,
|
Value: builtinIsCallable,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "to_json",
|
|
||||||
Value: builtinToJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "from_json",
|
|
||||||
Value: builtinFromJSON,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "type_name",
|
Name: "type_name",
|
||||||
Value: builtinTypeName,
|
Value: builtinTypeName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllBuiltinFunctionNames returns a list of all default builtin function names.
|
|
||||||
func AllBuiltinFunctionNames() []string {
|
|
||||||
var names []string
|
|
||||||
for _, bf := range Builtins {
|
|
||||||
names = append(names, bf.Name)
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBuiltinFunctions returns a slice of builtin function objects.
|
|
||||||
// GetBuiltinFunctions removes the duplicate names, and, the returned builtin functions
|
|
||||||
// are not guaranteed to be in the same order as names.
|
|
||||||
func GetBuiltinFunctions(names ...string) []*BuiltinFunction {
|
|
||||||
include := make(map[string]bool)
|
|
||||||
for _, name := range names {
|
|
||||||
include[name] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var builtinFuncs []*BuiltinFunction
|
|
||||||
for _, bf := range Builtins {
|
|
||||||
if include[bf.Name] {
|
|
||||||
bf := bf
|
|
||||||
builtinFuncs = append(builtinFuncs, &bf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return builtinFuncs
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllBuiltinFunctions returns all builtin functions.
|
|
||||||
func GetAllBuiltinFunctions() []*BuiltinFunction {
|
|
||||||
return GetBuiltinFunctions(AllBuiltinFunctionNames()...)
|
|
||||||
}
|
|
||||||
|
|||||||
8
vendor/github.com/d5/tengo/objects/bytes.go
generated
vendored
8
vendor/github.com/d5/tengo/objects/bytes.go
generated
vendored
@@ -79,3 +79,11 @@ func (o *Bytes) IndexGet(index Object) (res Object, err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate creates a bytes iterator.
|
||||||
|
func (o *Bytes) Iterate() Iterator {
|
||||||
|
return &BytesIterator{
|
||||||
|
v: o.Value,
|
||||||
|
l: len(o.Value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
57
vendor/github.com/d5/tengo/objects/bytes_iterator.go
generated
vendored
Normal file
57
vendor/github.com/d5/tengo/objects/bytes_iterator.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/compiler/token"
|
||||||
|
|
||||||
|
// BytesIterator represents an iterator for a string.
|
||||||
|
type BytesIterator struct {
|
||||||
|
v []byte
|
||||||
|
i int
|
||||||
|
l int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (i *BytesIterator) TypeName() string {
|
||||||
|
return "bytes-iterator"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *BytesIterator) String() string {
|
||||||
|
return "<bytes-iterator>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (i *BytesIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (i *BytesIterator) IsFalsy() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (i *BytesIterator) Equals(Object) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (i *BytesIterator) Copy() Object {
|
||||||
|
return &BytesIterator{v: i.v, i: i.i, l: i.l}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns true if there are more elements to iterate.
|
||||||
|
func (i *BytesIterator) Next() bool {
|
||||||
|
i.i++
|
||||||
|
return i.i <= i.l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the key or index value of the current element.
|
||||||
|
func (i *BytesIterator) Key() Object {
|
||||||
|
return &Int{Value: int64(i.i - 1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value of the current element.
|
||||||
|
func (i *BytesIterator) Value() Object {
|
||||||
|
return &Int{Value: int64(i.v[i.i-1])}
|
||||||
|
}
|
||||||
4
vendor/github.com/d5/tengo/objects/closure.go
generated
vendored
4
vendor/github.com/d5/tengo/objects/closure.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
|||||||
// Closure represents a function closure.
|
// Closure represents a function closure.
|
||||||
type Closure struct {
|
type Closure struct {
|
||||||
Fn *CompiledFunction
|
Fn *CompiledFunction
|
||||||
Free []*Object
|
Free []*ObjectPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
// TypeName returns the name of the type.
|
||||||
@@ -29,7 +29,7 @@ func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||||||
func (o *Closure) Copy() Object {
|
func (o *Closure) Copy() Object {
|
||||||
return &Closure{
|
return &Closure{
|
||||||
Fn: o.Fn.Copy().(*CompiledFunction),
|
Fn: o.Fn.Copy().(*CompiledFunction),
|
||||||
Free: append([]*Object{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers
|
Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
vendor/github.com/d5/tengo/objects/compiled_function.go
generated
vendored
11
vendor/github.com/d5/tengo/objects/compiled_function.go
generated
vendored
@@ -47,3 +47,14 @@ func (o *CompiledFunction) IsFalsy() bool {
|
|||||||
func (o *CompiledFunction) Equals(x Object) bool {
|
func (o *CompiledFunction) Equals(x Object) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SourcePos returns the source position of the instruction at ip.
|
||||||
|
func (o *CompiledFunction) SourcePos(ip int) source.Pos {
|
||||||
|
for ip >= 0 {
|
||||||
|
if p, ok := o.SourceMap[ip]; ok {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
ip--
|
||||||
|
}
|
||||||
|
return source.NoPos
|
||||||
|
}
|
||||||
|
|||||||
38
vendor/github.com/d5/tengo/objects/continue.go
generated
vendored
38
vendor/github.com/d5/tengo/objects/continue.go
generated
vendored
@@ -1,38 +0,0 @@
|
|||||||
package objects
|
|
||||||
|
|
||||||
import "github.com/d5/tengo/compiler/token"
|
|
||||||
|
|
||||||
// Continue represents a continue statement.
|
|
||||||
type Continue struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
|
||||||
func (o *Continue) TypeName() string {
|
|
||||||
return "continue"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Continue) String() string {
|
|
||||||
return "<continue>"
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinaryOp returns another object that is the result of
|
|
||||||
// a given binary operator and a right-hand side object.
|
|
||||||
func (o *Continue) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|
||||||
return nil, ErrInvalidOperator
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a copy of the type.
|
|
||||||
func (o *Continue) Copy() Object {
|
|
||||||
return &Continue{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFalsy returns true if the value of the type is falsy.
|
|
||||||
func (o *Continue) IsFalsy() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns true if the value of the type
|
|
||||||
// is equal to the value of another object.
|
|
||||||
func (o *Continue) Equals(x Object) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
25
vendor/github.com/d5/tengo/objects/conversion.go
generated
vendored
25
vendor/github.com/d5/tengo/objects/conversion.go
generated
vendored
@@ -1,6 +1,7 @@
|
|||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -158,8 +159,8 @@ func ToTime(o Object) (v time.Time, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// objectToInterface attempts to convert an object o to an interface{} value
|
// ToInterface attempts to convert an object o to an interface{} value
|
||||||
func objectToInterface(o Object) (res interface{}) {
|
func ToInterface(o Object) (res interface{}) {
|
||||||
switch o := o.(type) {
|
switch o := o.(type) {
|
||||||
case *Int:
|
case *Int:
|
||||||
res = o.Value
|
res = o.Value
|
||||||
@@ -176,13 +177,29 @@ func objectToInterface(o Object) (res interface{}) {
|
|||||||
case *Array:
|
case *Array:
|
||||||
res = make([]interface{}, len(o.Value))
|
res = make([]interface{}, len(o.Value))
|
||||||
for i, val := range o.Value {
|
for i, val := range o.Value {
|
||||||
res.([]interface{})[i] = objectToInterface(val)
|
res.([]interface{})[i] = ToInterface(val)
|
||||||
|
}
|
||||||
|
case *ImmutableArray:
|
||||||
|
res = make([]interface{}, len(o.Value))
|
||||||
|
for i, val := range o.Value {
|
||||||
|
res.([]interface{})[i] = ToInterface(val)
|
||||||
}
|
}
|
||||||
case *Map:
|
case *Map:
|
||||||
res = make(map[string]interface{})
|
res = make(map[string]interface{})
|
||||||
for key, v := range o.Value {
|
for key, v := range o.Value {
|
||||||
res.(map[string]interface{})[key] = objectToInterface(v)
|
res.(map[string]interface{})[key] = ToInterface(v)
|
||||||
}
|
}
|
||||||
|
case *ImmutableMap:
|
||||||
|
res = make(map[string]interface{})
|
||||||
|
for key, v := range o.Value {
|
||||||
|
res.(map[string]interface{})[key] = ToInterface(v)
|
||||||
|
}
|
||||||
|
case *Time:
|
||||||
|
res = o.Value
|
||||||
|
case *Error:
|
||||||
|
res = errors.New(o.String())
|
||||||
|
case *Undefined:
|
||||||
|
res = nil
|
||||||
case Object:
|
case Object:
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|||||||
31
vendor/github.com/d5/tengo/objects/count_objects.go
generated
vendored
Normal file
31
vendor/github.com/d5/tengo/objects/count_objects.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// CountObjects returns the number of objects that a given object o contains.
|
||||||
|
// For scalar value types, it will always be 1. For compound value types,
|
||||||
|
// this will include its elements and all of their elements recursively.
|
||||||
|
func CountObjects(o Object) (c int) {
|
||||||
|
c = 1
|
||||||
|
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *Array:
|
||||||
|
for _, v := range o.Value {
|
||||||
|
c += CountObjects(v)
|
||||||
|
}
|
||||||
|
case *ImmutableArray:
|
||||||
|
for _, v := range o.Value {
|
||||||
|
c += CountObjects(v)
|
||||||
|
}
|
||||||
|
case *Map:
|
||||||
|
for _, v := range o.Value {
|
||||||
|
c += CountObjects(v)
|
||||||
|
}
|
||||||
|
case *ImmutableMap:
|
||||||
|
for _, v := range o.Value {
|
||||||
|
c += CountObjects(v)
|
||||||
|
}
|
||||||
|
case *Error:
|
||||||
|
c += CountObjects(o.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
7
vendor/github.com/d5/tengo/objects/importable.go
generated
vendored
Normal file
7
vendor/github.com/d5/tengo/objects/importable.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// Importable interface represents importable module instance.
|
||||||
|
type Importable interface {
|
||||||
|
// Import should return either an Object or module source code ([]byte).
|
||||||
|
Import(moduleName string) (interface{}, error)
|
||||||
|
}
|
||||||
77
vendor/github.com/d5/tengo/objects/module_map.go
generated
vendored
Normal file
77
vendor/github.com/d5/tengo/objects/module_map.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// ModuleMap represents a set of named modules.
|
||||||
|
// Use NewModuleMap to create a new module map.
|
||||||
|
type ModuleMap struct {
|
||||||
|
m map[string]Importable
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewModuleMap creates a new module map.
|
||||||
|
func NewModuleMap() *ModuleMap {
|
||||||
|
return &ModuleMap{
|
||||||
|
m: make(map[string]Importable),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds an import module.
|
||||||
|
func (m *ModuleMap) Add(name string, module Importable) {
|
||||||
|
m.m[name] = module
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBuiltinModule adds a builtin module.
|
||||||
|
func (m *ModuleMap) AddBuiltinModule(name string, attrs map[string]Object) {
|
||||||
|
m.m[name] = &BuiltinModule{Attrs: attrs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSourceModule adds a source module.
|
||||||
|
func (m *ModuleMap) AddSourceModule(name string, src []byte) {
|
||||||
|
m.m[name] = &SourceModule{Src: src}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a named module.
|
||||||
|
func (m *ModuleMap) Remove(name string) {
|
||||||
|
delete(m.m, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns an import module identified by name.
|
||||||
|
// It returns if the name is not found.
|
||||||
|
func (m *ModuleMap) Get(name string) Importable {
|
||||||
|
return m.m[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBuiltinModule returns a builtin module identified by name.
|
||||||
|
// It returns if the name is not found or the module is not a builtin module.
|
||||||
|
func (m *ModuleMap) GetBuiltinModule(name string) *BuiltinModule {
|
||||||
|
mod, _ := m.m[name].(*BuiltinModule)
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourceModule returns a source module identified by name.
|
||||||
|
// It returns if the name is not found or the module is not a source module.
|
||||||
|
func (m *ModuleMap) GetSourceModule(name string) *SourceModule {
|
||||||
|
mod, _ := m.m[name].(*SourceModule)
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a copy of the module map.
|
||||||
|
func (m *ModuleMap) Copy() *ModuleMap {
|
||||||
|
c := &ModuleMap{
|
||||||
|
m: make(map[string]Importable),
|
||||||
|
}
|
||||||
|
for name, mod := range m.m {
|
||||||
|
c.m[name] = mod
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of named modules.
|
||||||
|
func (m *ModuleMap) Len() int {
|
||||||
|
return len(m.m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMap adds named modules from another module map.
|
||||||
|
func (m *ModuleMap) AddMap(o *ModuleMap) {
|
||||||
|
for name, mod := range o.m {
|
||||||
|
m.m[name] = mod
|
||||||
|
}
|
||||||
|
}
|
||||||
41
vendor/github.com/d5/tengo/objects/object_ptr.go
generated
vendored
Normal file
41
vendor/github.com/d5/tengo/objects/object_ptr.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObjectPtr represents a free variable.
|
||||||
|
type ObjectPtr struct {
|
||||||
|
Value *Object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ObjectPtr) String() string {
|
||||||
|
return "free-var"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *ObjectPtr) TypeName() string {
|
||||||
|
return "<free-var>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *ObjectPtr) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *ObjectPtr) Copy() Object {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *ObjectPtr) IsFalsy() bool {
|
||||||
|
return o.Value == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *ObjectPtr) Equals(x Object) bool {
|
||||||
|
return o == x
|
||||||
|
}
|
||||||
39
vendor/github.com/d5/tengo/objects/return_value.go
generated
vendored
39
vendor/github.com/d5/tengo/objects/return_value.go
generated
vendored
@@ -1,39 +0,0 @@
|
|||||||
package objects
|
|
||||||
|
|
||||||
import "github.com/d5/tengo/compiler/token"
|
|
||||||
|
|
||||||
// ReturnValue represents a value that is being returned.
|
|
||||||
type ReturnValue struct {
|
|
||||||
Value Object
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
|
||||||
func (o *ReturnValue) TypeName() string {
|
|
||||||
return "return-value"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *ReturnValue) String() string {
|
|
||||||
return "<return-value>"
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinaryOp returns another object that is the result of
|
|
||||||
// a given binary operator and a right-hand side object.
|
|
||||||
func (o *ReturnValue) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|
||||||
return nil, ErrInvalidOperator
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a copy of the type.
|
|
||||||
func (o *ReturnValue) Copy() Object {
|
|
||||||
return &ReturnValue{Value: o.Copy()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFalsy returns true if the value of the type is falsy.
|
|
||||||
func (o *ReturnValue) IsFalsy() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns true if the value of the type
|
|
||||||
// is equal to the value of another object.
|
|
||||||
func (o *ReturnValue) Equals(x Object) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
11
vendor/github.com/d5/tengo/objects/source_module.go
generated
vendored
Normal file
11
vendor/github.com/d5/tengo/objects/source_module.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
// SourceModule is an importable module that's written in Tengo.
|
||||||
|
type SourceModule struct {
|
||||||
|
Src []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import returns a module source code.
|
||||||
|
func (m *SourceModule) Import(_ string) (interface{}, error) {
|
||||||
|
return m.Src, nil
|
||||||
|
}
|
||||||
5
vendor/github.com/d5/tengo/objects/user_function.go
generated
vendored
5
vendor/github.com/d5/tengo/objects/user_function.go
generated
vendored
@@ -6,8 +6,9 @@ import (
|
|||||||
|
|
||||||
// UserFunction represents a user function.
|
// UserFunction represents a user function.
|
||||||
type UserFunction struct {
|
type UserFunction struct {
|
||||||
Name string
|
Name string
|
||||||
Value CallableFunc
|
Value CallableFunc
|
||||||
|
EncodingID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
// TypeName returns the name of the type.
|
||||||
|
|||||||
3
vendor/github.com/d5/tengo/runtime/errors.go
generated
vendored
3
vendor/github.com/d5/tengo/runtime/errors.go
generated
vendored
@@ -6,3 +6,6 @@ import (
|
|||||||
|
|
||||||
// ErrStackOverflow is a stack overflow error.
|
// ErrStackOverflow is a stack overflow error.
|
||||||
var ErrStackOverflow = errors.New("stack overflow")
|
var ErrStackOverflow = errors.New("stack overflow")
|
||||||
|
|
||||||
|
// ErrObjectAllocLimit is an objects allocation limit error.
|
||||||
|
var ErrObjectAllocLimit = errors.New("object allocation limit exceeded")
|
||||||
|
|||||||
2
vendor/github.com/d5/tengo/runtime/frame.go
generated
vendored
2
vendor/github.com/d5/tengo/runtime/frame.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
|||||||
// Frame represents a function call frame.
|
// Frame represents a function call frame.
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
fn *objects.CompiledFunction
|
fn *objects.CompiledFunction
|
||||||
freeVars []*objects.Object
|
freeVars []*objects.ObjectPtr
|
||||||
ip int
|
ip int
|
||||||
basePointer int
|
basePointer int
|
||||||
}
|
}
|
||||||
|
|||||||
774
vendor/github.com/d5/tengo/runtime/vm.go
generated
vendored
774
vendor/github.com/d5/tengo/runtime/vm.go
generated
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user